You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by "Sigurd S. Randal" <si...@bits-ikt.no> on 2004/12/06 13:05:30 UTC

Tree component in real use, anybody tried this?

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
<http://www.dsleeper.net/index.php?option=content&task=view&id=24&Itemid>
&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/>
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.