Sorry for posting twice, but here is the full post (Not just a link to a web page... For formated text please read here: http://www.dsleeper.net/index.php?option=content&task=view&id=24&Itemid=)

I'm using the Myfaces tree component as a menu in an extensive application for showing/editing company data. The tree nodes are initialized with company data as a root node. Then children -> departments -> workplaces -> employees etc.

Each of the nodes link to specific JSP datapages within the company. I'm quite new to implementing a complete JSF solution and have several problems with the one I'm currently working on.

Can anybody show me how to use a JSF tree component to achieve these goals:
-The tree is going to behave like a menu, where every node points to a page.
-The tree should not lose it's current open/close state when loading a new page
-When clicking one of the nodes in the tree, it should be able to pass request parameters to the page that is loading.

JSF MAINPAGE

The tree is implemented in a JSF page "mainpage.jsp" like this:


<body>
  <f:view>
    <table align="center" border="1" width="800" height="600">
      <tr valign="top" height="60">
        <td colspan="2" bgcolor="#9999FF">
          <f:subview id="topMenu">
            <c:import url="top.jsp"/>
          </f:subview>
        </td>
     </tr>
     <tr valign="top">
        <td width="300">
          <%-- THE TREE COMPONENT --%>

          <f:subview id="treeMenu">
             <x:tree id="tree" value="#{TreeMenu.tree}"
                                       styleClass="tree"
                                       nodeClass="treenode"
                                       selectedNodeClass="treenodeSelected"
                                       expandRoot="true">
               <x:treeSelectionListener type="mycompany.menus.TreeMenu"/>
             </x:tree>
          </f:subview>
       </td>
       <td width="500">
         <table border="0" align="center">
           <tr align="center">
             <td align="center">
              <%-- THE MAIN BLOCK, CONTAINS OTHER JSF DATAPAGES --%>
             
<f:subview id="${sessionScope.render}"> 
                 <c:import url="${sessionScope.render}"/>
             
</f:subview>
           
</td>
          
</tr>
         
</table>
       </td>
     </tr>
   <tr>
    
<td colspan="2" align="center" bgcolor="#9999FF" height="20">
       <f:subview id="footer">
         <c:import url="footer.jsp"/>
       </f:subview>
     </td>
  </tr>
 </f:view> 
</table>

THINGS TO NOTICE IN THE PRECEDING CODE:
-
The code is a mix of standard HTML, JSF and JSTL tags (I would like to know if this creates some sort of unwanted side effects)
-The main block of the table is going to show a JSF datapage in the center of the users screen and it is initialized via a session parameter containing the correct URL of that JSP

Other solutions that were tested:
-Having several subviews defined in the file and then initializing which one to render in view via the JSF component render attribute. The treeMenu backing bean would then have to include a boolean for every page in the site. I could not get this working because the attribute does not seem to be read at page reload. Therefore the first pageview was always shown even though the boolean values were changed in the backing bean.

THE BACKING BEAN - TreeMenu

faces-config.xml:
<managed-bean>
 
<managed-bean-name>TreeMenu</managed-bean-name>
 
<managed-bean-class>mycompany.menus.TreeMenu</managed-bean-class>
 
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>

Class implementation:
There are two helper classes heavily used in the tree, they are:
SiteNavigation - A class which is put into the nodes as the userObject. The class contains the relative URL that is going to be rendered and any parameters that needs to be set in the request/session to that datapage. The getRealURL() of this class returns the complete URL relative to the installation.
RequestParameter -  A simple class with two string values "paramName" & "param"

public class TreeMenu implements TreeSelectionListener
{
   private DefaultTreeModel tree;
   
   public TreeMenu()
    {
        super();
        this.init();
        //DEFINES DEFAULT RENDER PAGE WHEN MAINPAGE IS FIRST SHOWN
        FacesContext.getCurrentInstance().getExternalContext().getSessionMap().
        put("render","datapages/statsPage.jsp");
    }

    public void init()
    {
        c = new CompanyHandler().getCompany();

        //*************************ROOT NODE********************************
        root = new DefaultMutableTreeNode(c.getName());
        SiteNavigation rootNav = new SiteNavigation();
        rootNav.setURL("datapages/statsPage.jsp");
        rootNav.setName(c.getName());
        root.setUserObject(rootNav);

        //*************************ORGANIZATION NODE*************************
        organizationNode = new DefaultMutableTreeNode("Organization");
        SiteNavigation orgNav = new SiteNavigation();
        orgNav.setURL("datapages/organization.jsp");
        orgNav.setName("Organization");
        organizationNode.setUserObject(orgNav);
        root.insert(organizationNode);
       
       //SEVERAL MORE NODES ARE ADDED AFTER THIS..... 

       //EXAMPLE WITH SITENAVIGATION REQUESTPARAMETERS
       //ONE DEPARTMENT WORKPLACE
       Workplace wp = (Workplace) wpIterator.next();
       DefaultMutableTreeNode wpNode = new DefaultMutableTreeNode(wp .getName());
       SiteNavigation wpNav = new SiteNavigation();
       wpNav.setName(wp.getName());
       wpNav.setURL("datapages/workplacePage.jsp");
       wpNav.getParameters().add(new RequestParameter("wid", wp.getId().toString()));
       wpNav.getParameters().add( new RequestParameter("depid", dep.getId().toString()));
       wpNode.setUserObject(wpNav);

      //END OF INIT -> All NODES HAVE BEEN ADDED TO THE ROOT
      tree = new DefaultTreeModel(root);     
    }

    public void valueChanged(TreeSelectionEvent event)
    {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) event
                .getNewSelectionPath().getLastPathComponent();

        if (node == null)
        {
            log.debug("NO NODE");
        }
        else
        {
            try
            {
                Object nodeInfo = node.getUserObject();
                SiteNavigation s = (SiteNavigation) nodeInfo;
                               
                //PUT DATAPAGE VIEW INTO SESSION PARAMETER
                FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("render", s.getRealURL());
                                
                //ADD PARAMETERS IF ANY 
                Iterator i = s.getParameters().iterator();
                while (i.hasNext())
                {
                    RequestParameter rp = (RequestParameter) i.next();
                    //I TRIED THIS FIRST, BUT THE REQUEST VALUES NEVER REACH THE "render" JSP PAGE/BACKINGBEAN
                    
//FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put(rp.getParamName(), rp.getParam());
                   

                    FacesContext.getCurrentInstance().getExternalContext() .getSessionMap().put(rp.getParamName(), rp.getParam());
                }
            }
            catch (Exception e)
            {
               log.error("ERROR IN menuTree valueChanged",e);
            }
        }
    }
}

CURRENT PROBLEMS WITH THE SOLUTION:

1. Request values that are being set in the TreeMenu backing bean are not being fed to the backing bean of the datapage referenced in the c:import->sessionScope.render. Instead I have writen the values into a session parameter, which in turn is a bad idea seeing as many of the pages will use the same parameter and parameter name. I will have to remember to reinitialize these parameters if they are written/read to other places.

2. If a different backing bean (for example a datapage) writes a variable into the session.render attribute this value is never updated in the rendering of the mainpage. Example of datapage backing bean write: FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("render", "datapages/employee.jsp"); This means that if a backing bean writes ie. "datapages/employee.jsp" to the render session parameter, the employee.jsp page will be shown for the rest of the session even if you click other nodes in the tree. This makes the tree completely useless as the mainpage main block never updates.

Currently I have yet been able to find a complete tutorial on how to actually use the Myfaces tree component. What I need now is comments and solutions to the problems listed here, hopefully without me needing to rewrite much of the code. I know about the Myfaces example application and code, much of this is based on that. However that example does not show how to navigate using the nodes it only tells you how to fill it with data.

QUESTIONS
1.
What is the correct way of writing request values from a backing bean? Is "FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("render", "datapages/employee.jsp");" ok code?
2. Would it be possible to implement the myfaces tree within a frame based solution instead?
3. Are there any other tree componentes ready for use out there? I have not yet tried the component from http://www.ourfaces.net, it seems much more complicated than the Myfaces solution. Anybody tried it? Please give me an extended example if you have.

If there is too little information in this post to comment on, please let me know and I will try to update it.

Reply via email to