10 Replies Latest reply on Jul 23, 2008 12:00 AM by bartj07

    Bypass required="true"

    bartj07

      I've got a form with a bunch of fields mapped to an entity with various hibernate/jpa validators on the entity properties.  I need to allow the user to perform two actions on this form: 'Save' or 'Submit'.  For my purposes, 'Save' means that any fields they filled out should be validated and persisted.  The rest can stay null.  'Submit' means that ALL fields are required, will be validated, and persisted.  What is the best way to achieve this?


      I can't use required="true" on my fields, because then 'Save' will always fail.  For the same reason, I can't use any Hibernate/jpa validators to do the required/notnull/notempty restriction.  The worst case (as I see it), would be to check each property for null/empty in my 'Submit' action method.  I'd like to avoid that obviously.


      Thanks,
      Jeremy

        • 1. Re: Bypass required="true"

          There is really no easy way to do this, but for good reason.


          Essentially JSF tries to ensure that no invalid values are set into your model (the entities your JSF components are bound to).  Seam hooks into the validations phase, as you noticed, with its Hibernate Validator integration.  This simply ensures that the Hibernate bean validations get executed in the validations phase, not allowing the invalid values to be set into your model (instead of throwing an exception at persistence time).  When you specify bean validations such as @NotNull, @NotEmpty, you are stating that the entity should not be persisted without meeting those conditions as often these restrictions are reflected as constraints in the DB (especially @NotNull).


          As a side-note here, because of the way JSF performs validations, a field that is required must use required='true'.  The @NotNull and @NotEmpty would be effectively ignored until persistence time.


          If you really want to accomplish this you are going to have to rely on a custom solution like the one you have proposed and make sure your DB doesn't specify null restrictions on these fields.  Hope that helps.

          • 2. Re: Bypass required="true"
            mail.micke

            Perhaps you can add a checkbox which is bound to a boolean on your backing bean and use the same boolean EL expression  for all your required attributes?


            If you're using RichFaces ticking the checkbox could reRender the entire form and update it with concern to the tickedness of the checkbox.
            (Not quite sure how this will work out when the required EL is true... perhaps have this in another form but that would loose currently entered values...)


            The you could also get away with only having one command button:


            <h:commandButton value="#{backingBean.submit ? 'Submit':'Save'}" action="#{backingBean.saveOrSubmit}"/>
            



            -micke

            • 3. Re: Bypass required="true"
              tony.herstell1
              immediate="true"


              bypasses validation (you probably know that); but I think it may also skip setting the actual vars.



              <a4j:commandButton styleClass="general_form_button" action="#{mailingListController.cancel}"
                                       value="#{messages.general_button_cancel}" immediate="true" type="submit">
                                  </a4j:commandButton>




              You could put multiple forms on the page each with its own submit button; then the user knows they have only submitted those items (and dealt with the failed validation).


              I even had a tab panel once; each with its own submit button so the user could free from a business process and do the last bit first if they wanted to (how 90's doing a multi stage process in sequence!).


              Anyhow, the OVERALL submit button (one button to rule them all and in the darkness bind them) was disabled until the user had correctly filled in all the parts.


              I hope that makes sense.

              • 4. Re: Bypass required="true"

                bypasses validation (you probably know that); but I think it may also skip setting the actual vars.

                Yes, you won't get the form values as they are only held in the UI components.



                Perhaps you can add a checkbox which is bound to a boolean on your backing bean and use the same boolean EL expression for all your required attributes?

                Very clever micke ;)



                (Not quite sure how this will work out when the required EL is true... perhaps have this in another form but that would loose currently entered values...)

                You could combine the two to get the desired result:


                <h:selectBooleanCheckbox value="#{backingBean.submit}">
                  <a:support event="onclick" immediate="true" reRender="myForm"
                    action="#{backingBean.setSubmit(not backingBean.submit)}" />
                </h:selectBooleanCheckbox>



                As long as you don't use @NotNull and @NotEmpty on your entity fields and you will be fine.  Good luck!

                • 5. Re: Bypass required="true"
                  amitev

                  I have the same usecase here, so i've submitted a request to the jsf spec

                  • 6. Re: Bypass required="true"
                    bartj07

                    Thank you all for the good advice.  I took the boolean checkbox idea and ran with it. 


                    The first thing I did was change all the required="true" to required="#{bb.submitMode}" which defaults to false.  This essentially puts the page in 'Save'-mode.  I changed the 'Submit' button from action="#{bb.submit}" to action="#{bb.prepareForSubmit}" which sets submitMode=true.  This then rerenders the page in 'Submit'-mode. 


                    The problem is then, when does the action bb.submit get called, all validation run (w/ required="true"), and still provide a decent user experience?  I added a <rich:modalPanel showWhenRendered="true" rendered="#{bb.submitMode and empty org.jboss.seam.faces.facesMessages.currentMessages}">  This renders a popup when the page loads only in submitMode and when there are no error messages.  The popup has a confirmation message and an 'OK' button which actually calls action="#{bb.submit}".  If the validation fails it rerenders the page with error messages but doesn't show the popup again.  Hitting the 'Save' button does a similar mode-switch/confirmation popup to switch back to 'Save'-mode.


                    I think this is the best solution from a user experience perspective, because they are used to seeing popup confirmation windows.  The checkbox would be weird and unfamiliar for the user.


                    Thanks again,
                    Jeremy

                    • 7. Re: Bypass required="true"

                      Hi!


                      In my opinion, there is no good reason to make this so hard, this it is one of the main mistakes in JSF (and I hope it gets fixed in JSF 2.0). What is needed is validation groups, like those available for ASP.NET 2.0.


                      Fortunately, this guy has implemented validations groups for JSF (and fixed a lot of JSF validation mistakes). I haven't tried it, but it looks very promising, it would be great if richfaces included controls like those in commons-validator-ext.


                      Regards,


                      Luxspes

                      • 8. Re: Bypass required="true"

                        Mmm, in second thought, it seems that commons-validator-ext depends on the use Shale... I wonder how hard would it be to integrate it with Seam...

                        • 9. Re: Bypass required="true"
                          amitev

                          Validation zones are implemented in netbeans visual pack under name virtual forms.

                          • 10. Re: Bypass required="true"
                            bartj07

                            Although I agree with needing 'validation zones', that is not actually the scenario I'm talking about.  In my scenario, I need two different ways to validate the same fields.


                            BTW - I changed the way my switch mode/confirmation works.  Its easier to understand the code (in my opinion).



                            <a:commandButton styleClass="button" value="Submit" 
                                        action="#{requestAction.prepareForSubmit}" 
                                        immediate="true" reRender="request">
                                <rich:componentControl for="submitAlert" operation="show" event="onclick"/>
                            </a:commandButton>
                            
                            <rich:modalPanel id="submitAlert" styleClass="alert">
                                <f:facet name="header">
                                    <h:outputText value="Submit"/>
                                </f:facet>
                                <p>The system will now submit the request.</p>
                                <h:commandButton styleClass="button" value="OK" action="#{requestAction.submit}">
                                    <rich:componentControl for="submitAlert" operation="hide" event="onclick"/>
                                </h:commandButton> 
                            </rich:modalPanel>



                            This changes the flow of events slightly.  The popup is rendered first while the fields behind it are rerendered via ajax.