Class ValidateBeanTagHandler
- java.lang.Object
-
- javax.faces.view.facelets.TagHandler
-
- de.xima.fc.gui.taghandler.ValidateBeanTagHandler
-
- All Implemented Interfaces:
javax.faces.view.facelets.FaceletHandler
public class ValidateBeanTagHandler extends javax.faces.view.facelets.TagHandler
The<o:validateBean>
allows the developer to control bean validation on a per-UICommand
orUIInput
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.Usage
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}" validationGroups="com.example.MyGroup" />
Class level validation details
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):
- Cloning - Bean must implement the
Cloneable
interface and support cloning according to the rules of that interface. SeeCloneCopier
- Serialization - Bean must implement the
Serializable
interface and support serialization according to the rules of that interface. SeeSerializationCopier
- Copy constructor - Bean must have an additional constructor (next to the default constructor) taking a
single argument of its own type that initializes itself with the values of that passed in type. See
CopyCtorCopier
- New instance - Bean should have a public no arguments (default) constructor. Every official JavaBean
satisfies this requirement. Note that in this case no copy is made of the original bean, but just a new
instance is created. See
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 theCopier
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}" validationGroups="com.example.MyGroup" 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.Faces messages
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
usingshowMessageFor="@violating"
, and when no matching component can be found, the message will fallback to being added with client ID of the parentUIForm
.<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
. TheshowMessageFor
attribute does by design not have any effect whenvalidateMethod="actual"
is used.- Author:
- Bauke Scholtz, Arjan Tijms
- See Also:
BeanValidationEventListener
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static class
ValidateBeanTagHandler.CollectingValidator
-
Constructor Summary
Constructors Constructor Description ValidateBeanTagHandler(javax.faces.view.facelets.TagConfig config)
The tag constructor.
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description void
apply(javax.faces.view.facelets.FaceletContext context, javax.faces.component.UIComponent parent)
If the parent component has thevalue
attribute or is an instance ofUICommand
orUIInput
and is new and we're in the restore view phase of a postback, then delegate toprocessValidateBean(FacesContext, UIComponent)
.protected void
processValidateBean(javax.faces.context.FacesContext context, javax.faces.component.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.
-
-
-
Method Detail
-
apply
public void apply(javax.faces.view.facelets.FaceletContext context, javax.faces.component.UIComponent parent) throws IOException
If the parent component has thevalue
attribute or is an instance ofUICommand
orUIInput
and is new and we're in the restore view phase of a postback, then delegate toprocessValidateBean(FacesContext, UIComponent)
.- Throws:
IllegalArgumentException
- When thevalue
attribute is absent and the parent component is not an instance ofUICommand
orUIInput
.IOException
-
processValidateBean
protected void processValidateBean(javax.faces.context.FacesContext context, javax.faces.component.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.- Parameters:
context
- The involved faces context.component
- The involved component.- Throws:
IllegalStateException
- When the parent form is missing.
-
-