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?
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:
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">
.