Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform validation in JSF, how to create a custom validator in JSF

I would like to perform validation in some of my input components such as <h:inputText> using some Java bean method. Should I use <f:validator> or <f:validateBean> for this? Where can I read more about it?

like image 305
Aram Gevorgyan Avatar asked May 18 '11 16:05

Aram Gevorgyan


1 Answers

The standard way is to implement the Validator interface.

@FacesValidator("fooValidator")
public class FooValidator implements Validator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        // ...

        if (valueIsInvalid) {
            throw new ValidatorException(new FacesMessage("Value is invalid!"));
        }
    }

}

The @FacesValidator will register it to JSF with validator ID myValidator so that you can reference it in validator attribute of any <h:inputXxx>/<h:selectXxx> component as follows:

<h:inputText id="foo" value="#{bean.foo}" validator="fooValidator" />
<h:message for="foo" />

Whenever the validator throws a ValidatorException, then its message will be displayed in the <h:message> associated with the input field.

You can also use EL in validator attribute of any <h:inputXxx>/<h:selectXxx> component wherein you reference a managed bean method having exactly the same method signature (the same method arguments) as Validator#validate(). I.e. taking FacesContext, UIComponent and Object arguments in this order.

<h:inputText id="foo" value="#{bean.foo}" validator="#{bean.validateFoo}" />
<h:message for="foo" />
public void validateFoo(FacesContext context, UIComponent component, Object value) throws ValidatorException {
    // ...

    if (valueIsInvalid) {
        throw new ValidatorException(new FacesMessage("Value is invalid!"));
    }
}

This is only useful if the validator needs to access another property present in the same managed bean. If it doesn't need to, then this approach is considered tight-coupling (poor practice thus), and you should split out the validator to its own class implementing the Validator interface.

You can also use <f:validator> taghandler, which would be the only way if you intend to attach multiple validators on the same component:

<h:inputText id="foo" value="#{bean.foo}">
    <f:validator validatorId="fooValidator" />
</h:inputText>
<h:message for="foo" />

This will execute the @FacesValidator("fooValidator") shown above.

You can also use <f:validator binding> to reference a concrete validator instance somewhere in the EL scope, which can be specified and supplied the following way:

<h:inputText id="foo" value="#{bean.foo}">
    <f:validator binding="#{fooValidator}" />
</h:inputText>
<h:message for="foo" />
@Named("fooValidator")
public class FooValidator implements Validator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        // ...

        if (valueIsInvalid) {
            throw new ValidatorException(new FacesMessage("Value is invalid!"));
        }
    }

}

Note that thus @Named is being used instead of @FacesValidator. The old @ManagedBean is also supported here instead of @Named. Historically, this was a trick in order to be able to use @EJB and @Inject in a validator. See also How to inject in @FacesValidator with @EJB, @PersistenceContext, @Inject, @Autowired

Or this way, which in turn can easily be supplied as a lambda:

<h:inputText id="foo" value="#{bean.foo}">
    <f:validator binding="#{bean.fooValidator}" />
</h:inputText>
<h:message for="foo" />
public Validator getFooValidator() {
    return (context, component, value) -> {
        // ...

        if (valueIsInvalid) {
            throw new ValidatorException(new FacesMessage("Value is invalid!"));
        }
    };
}

Also here applies the same problem of tight-coupling when this validator doesn't need any other property from the same bean.

To get a step further, you can use JSR303 bean validation. This validates fields based on annotations. So you can have just a

@Foo
private String foo;

Without the need to explicitly register any validator in XHTML side. If you're using JPA for persistence, by default this validator will also be executed during insert/update in DB. Since it's going to be a whole story, here are just some links to get started:

  • Hibernate Validator - Getting started
  • JSF 2.0 tutorial - Finetuning validation

There's also a <f:validateBean> tag, but this is only useful if you intend to disable the JSR303 bean validation. You then put the input components (or even the whole form) inside <f:validateBean disabled="true">.

See also:

  • JSF doesn't support cross-field validation, is there a workaround?
  • How to perform JSF validation in actionListener or action method?
like image 137
BalusC Avatar answered Oct 16 '22 11:10

BalusC