validator method not work in the JSF custom compos

2019-05-29 07:42发布

问题:

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:

回答1:

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.



回答2:

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.



回答3:

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

}