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="jakarta.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 @jakarta.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 Faces 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 jakarta.validation.ConstraintViolation#getPropertyPath() Property Path of the
ConstraintViolation using showMessageFor="@violating". When no matching component can be found, the violation is not displayed and does not
fail validation, so that bean properties which are not bound to any input component in the form cannot block the form submit. A class level constraint
violation matches all components bound to the validated bean.
<h:form>
...
<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>
Since OmniFaces 3.14.28, a fallback can be specified after @violating, separated by a space. This can be @form, @all,
@global, or a space separated collection of client IDs. Class level constraint violations and violations without a matching component will then
fail validation and be shown for the fallback. With @all and client IDs, the components are invalidated and labeled as described above. With
@form and @global, the message is only added, without label and without invalidating any component.
<h:form id="formId">
...
<!-- Class level and unmatched messages are 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 @form" />
</h:form>
The showMessageFor attribute accepts at most two keywords, and only in the form of @violating followed by a fallback keyword. Any
other combination of keywords, or of keywords and client IDs, is logged as warning, and then only its first valid part is used. For example,
@all @global is treated as @all, and @violating @global @form is treated as @violating @global.
The showMessageFor attribute is new since OmniFaces 2.6 and it defaults to @form. When method="validateActual" is
used, the validated input components are not collected. In that case @all, also as fallback of @violating, does not show any
message, and @form and @global show the message without labels.
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:
jakarta.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}" />"
| Name | Required | Type | Description |
|---|---|---|---|
copier | false | jakarta.el.ValueExpression
(must evaluate to java.lang.String)
| Set the bean copy strategy to use in case 'value' attribute is specified and the 'method' attribute is set (or defaulted) to 'validateCopy'. This attribute is ignored when the 'value' attribute is unspecified, or when the 'method' attribute is not set to 'validateCopy'. |
disabled | false | jakarta.el.ValueExpression
(must evaluate to java.lang.String)
| A boolean value enabling page level determination of whether or not this validator is enabled. |
messageFormat | false | jakarta.el.ValueExpression
(must evaluate to java.lang.String)
| The faces message format. Any "{0}" placeholder will be substituted with the error message and any "{1}" placeholder will be substituted with the labels of all validated fields. |
method | false | jakarta.el.ValueExpression
(must evaluate to java.lang.String)
| Set the class level validation method, which can be either 'validateCopy' or 'validateActual'. Defaults to 'validateCopy'. This attribute is ignored when the 'value' attribute is unspecified. |
showMessageFor | false | jakarta.el.ValueExpression
(must evaluate to java.lang.String)
| The identifier for which this validator should show the message. Defaults to "@form" which is the parent UIForm. Other available values are "@all", "@invalid", "@global" and "@violating". Any other space separated value will be treated as client ID of UI input component. Since OmniFaces 3.14.28, "@violating" can be followed by a space separated fallback, e.g. "@violating @global", which will be used for class level constraint violations and violations without a matching component. |
validationGroups | false | jakarta.el.ValueExpression
(must evaluate to java.lang.String)
| A comma-separated list of validation groups. A validation group is a fully-qualified class name. |
value | false | jakarta.el.ValueExpression
(must evaluate to java.lang.String)
| If specified, then only the given bean will be validated at class level. If unspecified, then only the bean properties bound to the in the current request processed/executed input fields will be validated "as usual". |
Output generated by Vdldoc View Declaration Language Documentation Generator.