3 Replies Latest reply on Jun 2, 2010 7:02 PM by kragoth

    Preserve panelMenu state across redirects

    allenc

      In the past I avoided using RichFaces panelMenu because I coudn't get the state of the menu to preserve across redirects.I found several posts on the subject, but the solutions seemed complex to me. So I finally bit the bullet and came up with a simple approach.


      Here is the view:


          <h:form>
              <rich:panelMenu style="width:200px" expandSingle="true" selectedChild="#{panelPage}">
                  <rich:panelMenuGroup label="One" expanded="#{panelGroups[0]}" >
                      <rich:panelMenuItem id="test1" action="#{pmenu.go('test1')}" label="Test Page 1"  mode="server" /> 
                      <rich:panelMenuItem id="test2" action="#{pmenu.go('test2')}" label="Test Page 2"  mode="server"  /> 
                  </rich:panelMenuGroup>
                  <rich:panelMenuGroup label="Two" expanded="#{panelGroups[1]}">
                      <rich:panelMenuItem id="test3" action="#{pmenu.go('test3')}" label="Test Page 3"  mode="server"  /> 
                      <rich:panelMenuItem id="test4" action="#{pmenu.go('test4')}" label="Test Page 4"  mode="server" /> 
                  </rich:panelMenuGroup>
              </rich:panelMenu>
          </h:form>



      Here is the bean:


      @Name("pmenu")
      @Scope(ScopeType.SESSION)
      public class PanelMenuBean {
          @In(required=false) @Out Boolean[] panelGroups;
          @Out private String panelPage;
              
          public String go(String to) {
              this.panelPage = to;
              return "/" + to + ".xhtml";
          }
      
          public String init() {
              panelGroups = new Boolean[5];
              for (int i = 0; i < 5; i++)
                  panelGroups[i] = false;
              panelGroups[0] = true;
              return go("test1");
          }
      }



      The Seam component hosts an array of boolean values that hold the expanded/collapsed state of each group. The size of the array needs to match the number of groups.
      The go() method accepts a portion of the view id, e.g. for /test1.xhtml I pass test1. This value is outjected via panelPage and the panelMenu pulls it into the selectedChild property to highlight the active link. Each panelMenuItem id must match the corresponding view id.


      I use the init() method when I navigate in from another area in my app. It initializes the group array to expand the first group and collapse the rest, and returns the default view id.


      This approach allows me to include the menu in multiple pages using ui:include or put it in a template. Hope it can help someone else.


      Allen

        • 1. Re: Preserve panelMenu state across redirects
          kragoth

          Nice approach, but a tad annoying. :P I see you saving the state of the selected child in your code but I don't see how you are saving the state of the expanded panel. No where are you updating your array except in the init method. So as far as I can see group 1 will always be expanded after a redirect regardless of which panel I had expanded at the time of the redirect. Unless of course the selectedChild attribute forces that panel to be the open panel (I can't remember). In which case there's no point in having the array anyways.



          I have always found that fixed size arrays generally means I need to take another look at my design.


          I would have thought that by using some of the Richfaces built in client functions you could just pass the id of the panel that you have open into your go method. Store this id and then just do a normal string comparison to work out if the group should be rendered as expanded or not. Doing it this way (or similar) will avoid issues where a new developer has no idea that he has to go changed the size of some fixed size array to allow him to save the state of his new menu group.


          Anyways, just my personal opinion.

          • 2. Re: Preserve panelMenu state across redirects
            allenc

            Tim,


            I take it you didn't actually try my approach. The state of each panel group is injected into the panelGroups array when the form is posted. Hence the @In annotation on the array. When you say as far as I can see, you must be referring to your anaysis of the approach. If you try it, you will see that the expanded status of each group is preserved across re-directs. And no, the selectedChild attribute will not force the panel groups to do anything. All it will do is highlight the currently selected menu item.


            So no need to resort to client functions or id passing.

            • 3. Re: Preserve panelMenu state across redirects
              kragoth

              Hey Allen,


              No, I did not try your approach. But, I see where I made a mistake in the interpretation of your code. :)


              I still don't think a fixed size array is a good idea, but I do like the simple approach you have taken.


              I'm wondering if this approach could be integrated into the component itself.  Make your own implementation of the panel menu that counts how many child groups it has and creates an array of that size to store the values in. Making it completely dynamic would be sweet.


              In fact, now that I think about it, this actually might solve a really annoying problem that we have in our application. (Something that we have worked around but, is not optimal).


              If I get some spare time to work on the issue I might give your approach a go but integrate it into the component itself rather then on a Seam bean.


              But, ultimately this is probably the easiest and simplest approach to saving the state I've seen. So nice work. :)