Reputation: 2195
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?
Upvotes: 17
Views: 28698
Reputation: 1109522
The standard way is to implement the Validator
interface.
@FacesValidator("fooValidator")
public class FooValidator implements Validator<String> {
@Override
public void validate(FacesContext context, UIComponent component, String value) throws ValidatorException {
if (value == null || value.isEmpty()) {
// Let required="true" handle it.
}
// ...
if (valueIsInvalid) {
throw new ValidatorException(new FacesMessage("Value is invalid!"));
}
}
}
The component
argument represents the component to which the validator is attached, which is usually an instance of UIInput
. The value
argument represents the value to be validated, which is usually the submitted and converted value of the associated UIInput
component.
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" />
If you consider the value
invalid, then just throw ValidatorException
. This way the input field will be marked invalid (i.e. UIInput#isValid()
will return false
), and the bean property behind the value
attribute of the input field (the #{bean.foo}
) won't be updated with the submitted/converted value, and the message of the ValidatorException
will be displayed in the <h:message>
associated with the input field.
Do note that checking for null/blank value should be delegated to the required="true"
attribute like so:
<h:inputText id="foo" value="#{bean.foo}" validator="fooValidator"
required="true" requiredMessage="Please fill out foo" />
<h:message for="foo" />
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, String value) throws ValidatorException {
if (value == null || value.isEmpty()) {
// Let required="true" handle it.
}
// ...
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.
In case you wish to validate based on multiple input values and/or bean properties, much better is to pass them as <f:attribute>
of the component. More detail can be found in the answer to JSF doesn't support cross-field validation, is there a workaround?
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<String> {
@Override
public void validate(FacesContext context, UIComponent component, String value) throws ValidatorException {
if (value == null || value.isEmpty()) {
// Let required="true" handle it.
}
// ...
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<String> getFooValidator() {
return (context, component, value) -> {
if (value == null || value.isEmpty()) {
// Let required="true" handle it.
}
// ...
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">
.
Upvotes: 39