public class ValidateBean extends TagHandler
The <o:validateBean>
allows the developer to control bean validation on a per-UICommand
or UIInput
component basis, as well as validating a given bean at the class level.
The standard <f:validateBean>
only allows validation control on a per-form
or a per-request basis (by using multiple tags and conditional EL expressions in its attributes) which may end up in
boilerplate code.
The standard <f:validateBean>
also, despite its name, does not actually have any facilities to
validate a bean at all.
Some examples
Control bean validation per component
<h:commandButton value="submit" action="#{bean.submit}"> <o:validateBean validationGroups="javax.validation.groups.Default,com.example.MyGroup" /> </h:commandButton>
<h:selectOneMenu value="#{bean.selectedItem}"> <f:selectItems value="#{bean.availableItems}" /> <o:validateBean disabled="true" /> <f:ajax execute="@form" listener="#{bean.itemChanged}" render="@form" /> </h:selectOneMenu>
Validate a bean at the class level
<h:inputText value="#{bean.product.item}" /> <h:inputText value="#{bean.product.order}" /> <o:validateBean value="#{bean.product}" />
Since OmniFaces 3.8, nested properties are also supported with @javax.validation.Valid
cascade
<h:inputText value="#{bean.product.item}" /> <h:inputText value="#{bean.product.order}" /> <o:validateBean value="#{bean}" />
Whereby the product
property looks like this:
@Valid private Product product;
When using <o:validateBean method="validateCopy" />
(which is the default), then only beans, lists,
maps and arrays are considered as nested properties and the copied bean will be autopopulated with defaults. If this
fails, then consider creating a custom copier as instructed in next section.
In order to validate a bean at the class level, all values from input components should first be actually set on that bean and only thereafter should the bean be validated. This however does not play well with the JSF approach where a model is only updated when validation passes. But for class level validation we seemingly can not validate until the model is updated. To break this tie, a copy of the model bean is made first, and then values are stored in this copy and validated there. If validation passes, the original bean is updated.
A bean is copied using the following strategies (in the order indicated):
Cloneable
interface and support cloning according to the rules of that interface. See CloneCopier
Serializable
interface and support serialization according to the rules of that interface. See SerializationCopier
CopyCtorCopier
NewInstanceCopier
If the above order is not ideal, or if an custom copy strategy is needed (e.g. when it's only needed to copy a few fields for the validation)
a strategy can be supplied explicitly via the copier
attribute. The value of this attribute can be any of the build-in copier implementations
given above, or can be a custom implementation of the Copier
interface.
If the copying strategy is not possible due to technical limitations, then you could set method
attribute to "validateActual"
.
<o:validateBean value="#{bean.product}" method="validateActual" />
This will update the model values and run the validation after update model values phase instead of the validations
phase. The disadvantage is that the invalid values remain in the model and that the action method is anyway invoked.
You would need an additional check for FacesContext.isValidationFailed()
in the action method to see if it
has failed or not.
By default, the faces message is added with client ID of the parent UIForm
.
<h:form id="formId"> ... <h:message for="formId" /> <o:validateBean ... /> </h:form>
The faces message can also be shown for all invalidated components using showMessageFor="@all"
.
<h:form> <h:inputText id="foo" /> <h:message for="foo" /> <h:inputText id="bar" /> <h:message for="bar" /> ... <o:validateBean ... showMessageFor="@all" /> </h:form>
The faces message can also be shown as global message using showMessageFor="@global"
.
<h:form> ... <o:validateBean ... showMessageFor="@global" /> </h:form> <h:messages globalOnly="true" />
The faces message can also be shown for specific components referenced by a space separated collection of their
client IDs in showMessageFor
attribute.
<h:form> <h:inputText id="foo" /> <h:message for="foo" /> <h:inputText id="bar" /> <h:message for="bar" /> ... <o:validateBean ... showMessageFor="foo bar" /> </h:form>
The faces message can also be shown for components which match Property
Path of the ConstraintViolation
using showMessageFor="@violating"
, and when no matching component can
be found, the message will fallback to being added with client ID of the parent UIForm
.
<h:form id="formId"> ... <!-- Unmatched messages shown here: --> <h:message for="formId" /> ... <h:inputText id="foo" value="#{bean.product.item}" /> <!-- Messages where ConstraintViolation PropertyPath is "item" are shown here: --> <h:message for="foo" /> ... <o:validateBean ... value="#{bean.product}" showMessageFor="@violating" /> </h:form>
The showMessageFor
attribute is new since OmniFaces 2.6 and it defaults to @form
. The
showMessageFor
attribute does by design not have any effect when validateMethod="actual"
is used.
The faces message uses a predefined message format, which corresponds to the value of BeanValidator.MESSAGE_ID
in the message bundle. The default message format of {1}: {0}
prepends the labels of all the validated
fields. This is useful in the case of validating a single bean property, but sometimes confusing in the case of
validating a bean with many properties.
In a form containing properties like First Name, Last Name, Address, Zip Code, and Phone Number where at the bean level, at least one of the name fields must be non-null, overriding the message format can help make a more clear error message.
This can be done by overriding the BeanValidator.MESSAGE_ID
line in the message bundle:
javax.faces.validator.BeanValidator.MESSAGE = Errors encountered: {0}
However, this change affects all bean validation messages site-wide. In case you'd like to fine-tune the bean
validation message on a per-<o:validateBean>
-basis, then you can since OmniFaces 3.12 use the
messageFormat
attribute. Any {0}
placeholder will be substituted with the error message
and any {1}
placeholder will be substituted with the labels of all validated fields.
<!-- Displays: "First Name, Last Name, Address, Zip Code, Phone Number: First Name and Last Name cannot both be null" --> <o:validateBean /> <!-- Displays: "Errors encountered: First Name and Last Name cannot both be null" --> <o:validateBean messageFormat="Errors encountered: {0}" />"
BeanValidationEventListener
Modifier and Type | Class and Description |
---|---|
static class |
ValidateBean.CollectingValidator |
nextHandler, tag, tagId
Constructor and Description |
---|
ValidateBean(TagConfig config)
The tag constructor.
|
Modifier and Type | Method and Description |
---|---|
void |
apply(FaceletContext context,
UIComponent parent)
If the parent component has the
value attribute or is an instance of UICommand or
UIInput and is new and we're in the restore view phase of a postback, then delegate to
processValidateBean(FacesContext, UIComponent) . |
protected void |
processValidateBean(FacesContext context,
UIComponent component)
Check if the given component has participated in submitting the current form or action and if so, then perform
the bean validation depending on the attributes set.
|
getAttribute, getRequiredAttribute, toString
public ValidateBean(TagConfig config)
config
- The tag config.public void apply(FaceletContext context, UIComponent parent) throws IOException
value
attribute or is an instance of UICommand
or
UIInput
and is new and we're in the restore view phase of a postback, then delegate to
processValidateBean(FacesContext, UIComponent)
.IllegalArgumentException
- When the value
attribute is absent and the parent component is not
an instance of UICommand
or UIInput
.IOException
protected void processValidateBean(FacesContext context, UIComponent component)
context
- The involved faces context.component
- The involved component.IllegalStateException
- When the parent form is missing.Copyright © 2012–2022 OmniFaces. All rights reserved.