Customized Richfaces Tree


Yesterday I had to customize the Richfaces tree component, because my client wants a special layout. My solution is a little bit strange. I share it here for someone which is in the same situation😉 Here is the story …

Per default the rich:tree looks like a standard tree browser (i.e. explorer, eclipse, whatever):

rich_tree_standard

But I want this look:

rich_tree_customized

You see that the expand/collapse icon (rich_tree_expand) is on the same level with the node-icon (rich_tree_leaf). That’s very hard to fix this with CSS (it’s possible but i prefer my strange solution ;)). The Richfaces documentation describes which parts of a tree could be customize.

We have:

  • rich-tree-node-handle and rich-tree-node-handleicon – a td which contains a link and a image to expand/collapse the node (only possible for a node not a leaf)
  • rich-tree-node-icon – is a td which contains the image for a node (a node with children)
  • rich-tree-node-icon-leaf – is a td which contains the image for a leaf (a node without children)

I decide to move the expand/collapse icon from the handle-td to the icon-td and „simulate“ the user-click with Javascript:

rich_tree_expand_move

Listing 1 tree.xhtml:

<rich:tree id="tree"
              binding="#{treeBean.tree}"
              var="item"
              switchType="ajax"
              ajaxSubmitSelection="true" 
              toggleOnClick="false"
              showConnectingLines="false"
              disableKeyboardNavigation="true">
          
    <f:facet name="iconCollapsed">
       <!-- no image for collapsed --> 
       <rich:spacer width="0"  height="0" style="border: none;"/>
    </f:facet>
    <f:facet name="iconExpanded">
       <!-- no image for expanded --> 
       <rich:spacer width="0"  height="0" style="border: none;"/>
    </f:facet>
          
    
    <f:facet name="icon">
       <!--  use normal node icon to toggle expand/collapse -->
       <h:panelGroup>
          <h:graphicImage value="#{item.isLeaf ? '/images/leaf.gif' : '/images/collapsed.gif'}" 
                                 onclick="myToggleTreeNode(this);"
                                 rendered="#{!treeBean.isExpanded}"/>
          <h:graphicImage value="#{item.isLeaf ? '/images/leaf.gif' : '/images/expanded.gif'}" 
                                 onclick="myToggleTreeNode(this);"
                                 rendered="#{treeBean.isExpanded}"/>
       </h:panelGroup>
    </f:facet>
          
    <f:facet name="iconLeaf">
       <h:graphicImage value="/images/leaf.gif"/> 
    </f:facet>
          
    <rich:recursiveTreeNodesAdaptor roots="#{treeBean.roots}" var="item" nodes="#{item.children}">
       <rich:treeNode>
          <h:outputText value="#{item.name}"/>
       </rich:treeNode>
    </rich:recursiveTreeNodesAdaptor>
</rich:tree>

Listing 2 tree.js:

function myToggleTreeNode(element) {
  var elem = jQuery(element);
  // img -> span -> td
  var parent = elem.parent().parent();
  var elementId = parent.attr("id");
  // i.e. j_id31:tree:j__id39:18::j_id40:icon -> the td arround the icon-image
  var index = elementId.lastIndexOf(":icon");
  var treeNodeId = elementId.substring(0, index);
  // i.e. j_id31:tree:j__id39:18::j_id40:handle -> the td arround the original expand/collapse-image
  var handleId = treeNodeId+':handle';
  // pure jQuery not working here
  var expandElement = jQuery($(handleId));
  expandElement.trigger("click");
}

Listing 3 tree.css:

.rich-tree-node-handleicon {
  display: none;
}