5 Replies Latest reply: Dec 5, 2012 6:33 AM by Mriem-Oracle RSS

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

    jmsjr
      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
          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
            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
              Mriem-Oracle
              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
                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
                  Mriem-Oracle
                  You can wrap it around multiple components and then it will be active for all those components.