10 Replies Latest reply on Sep 7, 2009 3:17 AM by Nikolay Elenkov

    How to validate all tabs of rich:tabPanel before form submission?

    Konstantin Larionov Newbie

      Hello,


      I need to handle Hibernate validation exception when form with rich:tabPanel is being submitted.
      I have the following simple form:


      <h:form id="organizationEditor">
      
           <h:panelGrid columns="1">
           
                <s:decorate id="nameField" template="edit.xhtml">
                     <ui:define name="label">Name</ui:define>
                     <h:inputText id="name" value="#{editedOrganization.name}" required="true">
                          <a4j:support id="nameCheck" event="onblur" ajaxSingle="true" reRender="nameField" />
                          <s:validate />
                     </h:inputText>
                </s:decorate>
                
           <!-- Other similar fields-->
           
           </h:panelGrid>
           
           <rich:tabPanel id="tabsPanel" switchType="ajax" immediate="true">
                <rich:tab label="Contacts">
                     <!-- Some optional fields -->
                </rich:tab>
                
                <rich:tab label="Supplementary">
                     <h:panelGrid columns="1">
                     
                          <!-- Required field -->
                          <s:decorate id="typeField" template="edit.xhtml">
                               <ui:define name="type">Type</ui:define>
                               <h:inputText id="name" value="#{editedOrganization.orgType}" required="true">
                                    <a4j:support id="typeCheck" event="onblur" ajaxSingle="true" reRender="typeField" />
                                    <s:validate />
                               </h:inputText>
                          </s:decorate>
      
                     </h:panelGrid>
                </rich:tab>
                
           </rich:tabPanel>
           <h:commandButton action="#{organizationEditor.save}" value="Save" />
           <h:commandButton action="#{organizationEditor.cancel}" value="Cancel" immediate="true" />
      </h:form>
      



      edit.xhtml is the following:


      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
           xmlns:s="http://jboss.com/products/seam/taglib"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:c="http://java.sun.com/jsf/core"
           xmlns:rich="http://richfaces.org/rich"
           xmlns:a4j="http://richfaces.org/a4j"
           xmlns:h="http://java.sun.com/jsf/html">
      
           <s:label styleClass="name #{invalid?'errors':''}">
                <ui:insert name="label"/>
                <s:span styleClass="required" rendered="#{required}">*</s:span>
           </s:label>
           <span class="value #{invalid?'errors':''}">
           <s:validateAll>
                <ui:insert/>
           </s:validateAll>
           </span>
           <s:message styleClass="error errors"/>
      
      </ui:composition>
      



      Now, when I press the Save button with the first (Contacts) tab active, without activating the second (Supplementary ) tab and filling all required fields with values, I receive Hibernate validator's exception:
      org.hibernate.validator.InvalidStateException with message: validation failed for: com.ims.ipat.core.entity.organizations.Organization


      It is correct since NotNull validator is defined for the Organization.orgType field.
      But I need to design my application so, that all fields are validated in web, without any exceptions :-)


      Could you, please, give me idea how such cases are usually handled?
      I'm not very experienced in web interfaces development with JSF and RichFaces, that's why I usually need some help in such common cases :-(

        • 1. Re: How to validate all tabs of rich:tabPanel before form submission?
          Arbi Sookazian Master

          If you must use tabs, and if the Hibernate Validator is correct, then perhaps you can set the focus to the tab that has the validation error.  But the problem then becomes, what do you do if there is a validation error on more than one tab?  Which tab would you set the focus to?  Or can you validate only one tab at a time per form submission?


          I used tabs more frequently in my Powerbuilder days than now.  You should post this question in the RF forum.  It's really a matter of how to handle validation errors with the tab component specifically.  I'm interested in what the best practice for this scenario is as this is a very good question.


          Practical RF book does not address your question/concern...

          • 2. Re: How to validate all tabs of rich:tabPanel before form submission?
            Nikolay Elenkov Master

            You are getting an error at persist time, because the form gets submitted even though not all required fields are set.
            You need to wrap the whole thing in <s:validateAll> to stop the form from being submitted if there are errors.
            This will not play nicely with <s:decorate> though, so you might have to give up on that. Something like:


            <s:validateAll>
             <h:panelGrid>
             </h:panelGrid>
            
              <rich:tabPanel>
                <rich:tab>
                  ...
                </rich:tab>
                ....
              </rich:tabPanel>
            </s:valdiateAll>
            



            • 3. Re: How to validate all tabs of rich:tabPanel before form submission?
              Arbi Sookazian Master

              Dynamic live validation using a4j:support event="onblur" would help as well.

              • 4. Re: How to validate all tabs of rich:tabPanel before form submission?
                Konstantin Larionov Newbie

                Well, I checked the JBoss RichFaces forum at JBoss.com at found the same question:


                JBoss.com - JBoss RichFaces - RichFaces Users - On Richface TabPanel, validation not happening on previous t


                According to Ilya Shaikovsky's (who seems to be involved in the RF development) answers it seems that it is possible to validate all tabs with client side switch type only. With any other switch type - all that is available on the client at any moment (and all what will be processed after submit) is current tab.


                So, I set attribute switchType to client and looks like validation is working properly now.


                After that according to Ilya's suggestion I tried to implement ajaxListener on the submit button to switch automatically to the tab which caused validation error.
                In this listener I perform checking for messages for components inside some of tabs. And if found, I try to make the first tab found selected.


                Unfortunately, ((HtmlTab) child).setActive(true) did not help. Probably, it is due to the fact that tabs are switched at client and some JavaScript function should be executed instead. I wish I knew which one and how...
                Can anyone help me with the ajaxListener's code?


                Submit button:


                ...
                <a4j:commandButton action="#{organizationEditor.save}" value="#{messages['editor.Save']}" reRender="organizationEditor">
                     <a4j:ajaxListener type="myPackage.Listener" />
                </a4j:commandButton>
                ...
                



                ajaxListener:


                package myPackage;
                
                import java.util.Iterator;
                
                import javax.faces.component.UIComponent;
                import javax.faces.context.FacesContext;
                
                import org.ajax4jsf.event.AjaxEvent;
                import org.ajax4jsf.event.AjaxListener;
                import org.jboss.seam.log.Log;
                import org.jboss.seam.log.Logging;
                import org.richfaces.component.html.HtmlTab;
                
                import com.ims.ipat.core.epr.PluginScanner;
                
                public class Listener implements AjaxListener {
                
                    private final Log log = Logging.getLog(PluginScanner.class);
                
                    private static UIComponent getParentHtmlTab(UIComponent component) {
                        if (component instanceof HtmlTab)
                            return component;
                
                        UIComponent htmlTabComponent = null;
                        if (component.getParent() != null) {
                            htmlTabComponent = getParentHtmlTab(component.getParent());
                        }
                
                        return htmlTabComponent;
                    }
                
                    public void processAjax(AjaxEvent arg0) {
                
                        FacesContext f = FacesContext.getCurrentInstance();
                
                        Iterator<String> itClientIds = f.getClientIdsWithMessages();
                        while (itClientIds.hasNext()) {
                            UIComponent component = f.getViewRoot().findComponent(itClientIds.next());
                            UIComponent htmlTab = getParentHtmlTab(component);
                            if (htmlTab != null) {
                                log.info("Highlighting HtmlTab with error");
                                ((HtmlTab) htmlTab).setActive(true); // This does not work
                                return;
                            }
                        }
                    }
                
                }
                



                • 5. Re: How to validate all tabs of rich:tabPanel before form submission?
                  Nikolay Elenkov Master

                  Is your listener being called? Might be just a typo, but 


                  <a4j:ajaxListener type="myPackage.Listener" />
                  



                  should be


                  <a4j:ajaxListener type="myPackage.AjaxListener" />
                  



                  Other than that, no idea. Again, better ask on the RF forum. BTW, what we did for a similar requirement
                  is change the dynamically set the tab CSS, so that tabs with errors are displayed with headers in red.

                  • 6. Re: How to validate all tabs of rich:tabPanel before form submission?
                    Konstantin Larionov Newbie

                    Is your listener being called? Might be just a typo, but 


                    Yes, sure :-)



                    BTW, what we did for a similar requirement
                    is change the dynamically set the tab CSS, so that tabs with errors are displayed with headers in red.


                    Could you, please, show me what CSS style was applied and how?


                    I tried to do the same by using ((HtmlTab) htmlTab).setStyle(...) and it worked, but not 100% properly.


                    My style was applied not only to tab header, but to tab's content as well.

                    • 7. Re: How to validate all tabs of rich:tabPanel before form submission?
                      Nikolay Elenkov Master

                      The xhtml is something like this:


                      <rich:tabPanel switchType="client" id="topTab">
                        <rich:tab label="tab1"
                                  id="tab1" 
                                  name="tab1"
                                  styleClass="#{styleUtils.getStyleByTab('tab1')}">
                      ...
                      



                      And then in StyleUtils we have:


                      public String getStyleByTab(String id{
                           if (FacesMessages.instance().getCurrentMessages().size() == 0) {
                               return "";
                           }
                      
                           UIComponent component = FacesContext.getCurrentInstance().getViewRoot()
                                   .findComponent("myform:" + id);
                      
                           boolean error = checkAll(component);
                           if (error) {
                               return "error-tab-label";
                            }
                      
                           return "";
                      }
                      



                      The checkAll method is very similar to your code above, checking for components with attached error messages.


                      The CSS is simply:


                      .error-tab-label {
                           color: red;
                      }
                      

                      • 9. Re: How to validate all tabs of rich:tabPanel before form submission?
                        Matteo Cusmai Newbie

                        Hello, for me doesn't works, because i am using an ear packaging, and richfaces-ui.jar is on war and my StyleUtils is on jar.
                        Can you help me?


                        Bye bye.

                        • 10. Re: How to validate all tabs of rich:tabPanel before form submission?
                          Nikolay Elenkov Master

                          Move your richfaces libs to the lib directory in your EAR.


                          myapp.ear
                            |
                            +- lib/
                                |
                                +- richfaces-api.jar
                                |
                                +- richfaces-ui.jar
                                |
                                +- richfaces-impl.jar
                          



                          HTH