This discussion is archived
5 Replies Latest reply: Dec 5, 2012 6:33 AM by 960507 RSS

Cross-Field validation with JSF2 and JSR-303 and h:message

jmsjr Newbie
Currently Being Moderated
JSR-303 allows you to do cross-field validation by annotating the class itself instead of annotating a specific property in the backing bean. That is all fine and good, and the validation message is also rendered as part of h:messages.

However, there are some requirements where
1) We need cross-field validation
2) But have the validation message "attached" ( for the lack of a better word ) to a specific property, and thus placed just beside the component in a JSF page.

Using JSR-303 annotations at the class level will NOT show the validation message using <h:message for="xxx"/>, it can only be rendered with <h:messages/>.

For example, some common cross-field validation requirements are:

1) If property A is not null, or is a specific value .. then property B is required.
2) If [1] is true, and B is null or blank, then show the validation message beside the component that is displaying property B.

Since cross-field validation is done with annotations at the class level, and cannot be done at the property level, the validation message for [2] can only be shown with <h:messages/> and not <h:message/>.

To work around this, what I have done is implement a custom (non-JSR303) validator, a javax.faces.validator.Validator, and use that validation in a <f:validator/> tag, as below:
<h:selectOneMenu id="DTHf_causeOfDeath" value="#{insuranceCase.causeOfDeath}" requiredMessage="Cause of Death is required">
     <f:selectItem itemValue="" itemLabel="-Select-" /> 
     <f:selectItems 
                  value="#{facesContext.externalContext.applicationMap['CAUSEOFDTH']}" var="causeOfDeath"
                  itemValue="#{causeOfDeath.code}" 
                  itemLabel="#{causeOfDeath.description}"/>
                  
             <f:validator validatorId="crossFieldRequiredValidator"/>
             <f:attribute name="requiredByComponentId" value="caseForm:f_clmTyp"/>
             <f:attribute name="requiredByComponentValue" value="DTH"/>
</h:selectOneMenu>
The actually validator expects to be supplied:
1) A parameter called "requiredByComponentId", which identifies which other component requires the current component
2) A parameter called "requiredByComponentValue", which specifies the condition or value of the "requiredByComponent" when the current component will be required.

NOTE:
3) I have specified the "requiredMessage" attribute, but NOT the "required" attribute as whether or not it is required is now determined dynamically by the custom validator.
4) The "requiredMessage" attribute is still used by the validator to add the actual message into the FacesContext when creating a FacesMessage.

With this approach, I can now use <h:message/> and have the validation message right beside the component that is dynamically determined to be required.

Though it works, I now have multiple layers of validation: JSR-303 validation and now <f:validator/>. I was hoping that I can use JSR-303 all throughout without having to use <f:validator/>

What have others done in this scenario ?

Edited by: jmsjr on 03-Dec-2012 22:13
  • 1. Re: Cross-Field validation with JSF2 and JSR-303 and h:message
    r035198x Pro
    Currently Being Moderated
    When I've had to cross field validate I've manually added the messages through FacesContext selecting the id of the component.
    The actual interaction with FacesContext happens in a JSFAdapter class though so the rest of my controller logic is still free of JSF. Not sure which of the two approaches is cleaner.
  • 2. Re: Cross-Field validation with JSF2 and JSR-303 and h:message
    jmsjr Newbie
    Currently Being Moderated
    r035198x wrote:
    When I've had to cross field validate I've manually added the messages through FacesContext selecting the id of the component.
    The actual interaction with FacesContext happens in a JSFAdapter class though so the rest of my controller logic is still free of JSF. Not sure which of the two approaches is cleaner.
    Thanks. Now at least I know I am not alone in this regard.

    I was looking at MyFaces Ext-val a week ago, and it "seems" to allow EL expressions in the annotation itself so that you can annotate a property in your bean and have it "depend" on another property .. but given the age of the project, and have not had any updates for sometime now .. I just get a lot of JAR dependencies (e .g. cglib, etc .. which I cannot use as I have to use javassist ) that I would not rather have. Documentation is not very helpful either.
  • 3. Re: Cross-Field validation with JSF2 and JSR-303 and h:message
    960507 Newbie
    Currently Being Moderated
    Have you considered wrapping your validator around your components? This way you would be able to write one validator that takes care of your specific requirements.

    Eg.

    <f:validator validatorId="crossFieldRequiredValidator">
    <h:selectOneMenu id="DTHf_causeOfDeath" value="#{insuranceCase.causeOfDeath}" requiredMessage="Cause of Death is required">
         <f:selectItem itemValue="" itemLabel="-Select-" />
         <f:selectItems
                   value="#{facesContext.externalContext.applicationMap['CAUSEOFDTH']}" var="causeOfDeath"
                   itemValue="#{causeOfDeath.code}"
                   itemLabel="#{causeOfDeath.description}"/>               
              <f:attribute name="requiredByComponentId" value="caseForm:f_clmTyp"/>
              <f:attribute name="requiredByComponentValue" value="DTH"/>
    </h:selectOneMenu>
    </f:validator>
  • 4. Re: Cross-Field validation with JSF2 and JSR-303 and h:message
    jmsjr Newbie
    Currently Being Moderated
    mojarra wrote:
    Have you considered wrapping your validator around your components? This way you would be able to write one validator that takes care of your specific requirements.

    Eg.

    <f:validator validatorId="crossFieldRequiredValidator">
    <h:selectOneMenu id="DTHf_causeOfDeath" value="#{insuranceCase.causeOfDeath}" requiredMessage="Cause of Death is required">
         <f:selectItem itemValue="" itemLabel="-Select-" />
         <f:selectItems
                   value="#{facesContext.externalContext.applicationMap['CAUSEOFDTH']}" var="causeOfDeath"
                   itemValue="#{causeOfDeath.code}"
                   itemLabel="#{causeOfDeath.description}"/>               
              <f:attribute name="requiredByComponentId" value="caseForm:f_clmTyp"/>
              <f:attribute name="requiredByComponentValue" value="DTH"/>
    </h:selectOneMenu>
    </f:validator>
    No .. I did not even thought that was possible / allowed .. but from what you said, that would be specific to that component only ??
    I would prefer that the validator be reusable across multiple components, allowing myself to specify which component the current component is dependent on.
  • 5. Re: Cross-Field validation with JSF2 and JSR-303 and h:message
    960507 Newbie
    Currently Being Moderated
    You can wrap it around multiple components and then it will be active for all those components.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points