JSF custom composite components
input.xhtml
<cc:interface>
<cc:attribute name="validator"/>
</cc:interface>
<cc:implementation>
<h:inputText validator="#{cc.attrs.validator}"/>
</cc:implementation>
*.xhtml
<l:input value = ... validator="#{testValidator.validator}"/>
java code
@ManagedBean
public class TestValidator {
public void validator(FacesContext context, UIComponent component, Object value) throws ValidatorException {
System.out.println("Call validator");
}
}
PropertyNotFoundException:
validator="#{testValidator.validator}": The class 'TestValidator' does not have the property 'validator'.
How to solve this problem?
my final way:
This is indeed not going to work. In order to attach a validator to an input component specified by the composite, you need to register the input component as a <cc:editableValueHolder>
in the <cc:interface>
first.
<cc:interface>
<cc:editableValueHolder name="yourInputName" targets="yourInputId" />
</cc:interface>
<cc:implementation>
<h:inputText id="yourInputId" ... />
</cc:implementation>
This way, any <f:validator for="yourInputName">
nested in the composite component declaration will be applied to the desired input component.
<l:input>
<f:validator validatorId="myValidator" for="yourInputName" />
</l:input>
You'll only need to replace the tight coupled validator method by a real standalone Validator
implementation.
@FacesValidator("myValidator")
public class MyValidator implements Validator {
// ...
}
Note: the standard JSF validators like <f:validateLength>
, <f:validateRequired>
, etc have all also a for
attribute for this purpose.
You'll only need to replace the tight coupled validator method by a real standalone Validator implementation.
BalusC, what if I like my tight coupled validator, for example if validation of this component depends on some state kept in my backing bean? I know this isn't proper componentized design, but it is what it is, which is temporary stop-gap solution to keep our backend developers going until I can develop a more full-featured component.
You need to define a cc:attribute "validator" with targets
attribute:
<cc:interface>
<cc:attribute name="validator" targets="inputId"/>
</cc:interface>
<cc:implementation>
<h:inputText id="inputId"/>
</cc:implementation>
(Notice that I don't define validator attribute in h:inputText)
This is easy as long as you don't implement component class for your composite component. Well... even then it is easy, but it won't work if your component class extends UIInput
. It works if it extends UINamingComponent
class (I am not sure if this is the best solution, or if you can make it work with UIInput but it works).
So if you want to have your own component class try this one:
<cc:interface componentType="myComponent>
<cc:attribute name="validator" targets="inputId"/>
</cc:interface>
<cc:implementation>
<h:inputText id="inputId"/>
</cc:implementation>
@FacesComponent("myComponent")
public class MyComponent extends UINamingContainer {
@Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
//your code here
}