The ICEfaces Tutorial
Table of Contents


Lesson: Using the tree component

How to Use the ICEfaces Tree Component

The tree component can be used to display hierarchical data. The tree component renders its view using a javax.swing.tree.DefaultTreeModel Object as a model. Client side Swing developers who are familiar with the DefaultTreeModel and the DefaultMutableTreeNode class will find an easy migration path to the ice:tree component. The following screen shot is of the ice:tree component using the CSS XP theme.

Tree component in Webmail Application

The tree displays its data vertically where every node of the tree is represented by a DefaultMutableTreeNode. A node can either be a leaf or branch depending if it has zero or more child nodes. A user can click on the branch expand/contract icon to toggle the visibility of child nodes. The root tree node visibility can be togged by setting the the tree hideRootNode attribute to false. Each DefaultMutableTreeNode wraps an IceUserObject which contains instance variables for common tree node node properties such as icon paths, tool tips, display text and expanded state.

The rest of this tutorial will discuss the following topics:

 

Creating a Tree

Creating a basic tree component is a pretty standard process comprising of two parts. The first part consists of building a backing bean which will contain the DefaultTreeModel. The second part is to add thetree component to your ICEfaces application. Here is a picture of a rendered tree which will be created.

Basic Tree Example

The following code, is taken from TreeBean.java which builds a simple DefaultTreeModel, one root node with three child nodes. The TreeBean must expose the DefaultTreeModel instance variable as it must be linked to the tree components' value attributes.

    // create root node with its children expanded
    DefaultMutableTreeNode rootTreeNode = new DefaultMutableTreeNode();
    IceUserObject rootObject = new IceUserObject(rootTreeNode);
    rootObject.setText("Root Node");
    rootObject.setExpanded(true);
    rootTreeNode.setUserObject(rootObject);

    // model is accessed by by the ice:tree component via a getter method
    model = new DefaultTreeModel(rootTreeNode);

    // add some child nodes
    for (int i = 0; i <3; i++) {
        DefaultMutableTreeNode branchNode = new DefaultMutableTreeNode();
        IceUserObject branchObject = new IceUserObject(branchNode);
        branchObject.setText("node-" + i);
        branchNode.setUserObject(branchObject);
        rootTreeNode.add(branchNode);
    }

Download the demo:

The TreeBean must be instantiated in the faces-config.xml so that we can use it in our JSF application. Here is the JSF code that is needed to render the DefaultTreeModel defined in TreeBean:

    <ice:tree id="tree"
        value="#{tree.model}"
        var="item"
        hideRootNode="false"
        hideNavigation="false"
        imageDir="./xmlhttp/css/xp/css-images/" >
    <ice:treeNode>
        <f:facet name="content">
            <ice:panelGroup style="display: inline">
                <ice:outputText value="#{item.userObject.text}" />
            </ice:panelGroup>
        </f:facet>
    </ice:treeNode>
    </ice:tree>

Notice how the tree component has a treeNode child component. This child component is sub-child further with a facets named content. The facet name is self descriptive and allows for any type of JSF component to be added to them. There is also an icon facet which should be used to hold components related to a tree node icon. The content facet should be used to hold components related to a tree nodes text label. The tree tag is iterative and will apply its child component hierarchy to all of the DefaultMutableTreeNode found in the DefaultTreeModel.

To summaries the tree component can be created with a simple binding to a backing bean which contains a DefaultTreeModel. You do not have to write any code to make the tree nodes expand and contract.

 

Customising the IceUserObject

The IceUserObject object was designed to have a bare minimum of instance variables needed to display a basic tree node. In this next example the IceUserObject will be extended so that it can store a String representing a URL. The following code is taken from UrlNodeUserObject.java. private String url;

    public UrlNodeUserObject(DefaultMutableTreeNode wrapper) {
        super(wrapper);
    }

    public String getUrl() {
        return url;
    }

Now that every node can have a unique URL we need to update the JSF code in the linkTree.jspx file. The following code shows how to add a commandLink to an treeNode which takes advantage of our new UrlNodeUserObject object.

    <ice:treeNode>
        <f:facet name="content">
            <ice:panelGroup style="display: inline">
                <ice:outputLink value="#{item.userObject.url}" target="_blank">
                    <ice:outputText value="#{item.userObject.text}" />
                </ice:outputLink>
           </ice:panelGroup>
        </f:facet>
    </ice:treeNode>

Download the demo:

Customising the IceUserObject is quite simple and when it is combined with the iterative nature of the tree component, it can be quite a powerful tool. The next section will show how to respond to node selection.

 

Responding to Node Selection

In this next example the IceUserObject will be extended so that it can respond to node selection and change the selected panel in a panelStack component. The tree component does not have a selected node event listener, instead the commandLink component will be use to respond to a users mouse click. The following code shows how the PanelSelectUserObject default constructor has been modified to get a reference to the PanelStackBean responsible for selecting the PanelStack components selected panel:

    public PanelSelectUserObject(DefaultMutableTreeNode wrapper) {
        super(wrapper);
        // get a reference to the PanelStackBean from the faces context
        FacesContext facesContext = FacesContext.getCurrentInstance();
        Object panelStackObject =
                facesContext.getApplication()
                        .createValueBinding("#{panelStack}")
                        .getValue(facesContext);
        if (panelStackObject instanceof PanelStackBean){
            panelStack = (PanelStackBean)panelStackObject;
        }
    }

The PanelSelectUserObject needs to be able to respond to a user click and update the the internal state of the backing beans. The user clicks are handled by binding a method to the tree nodes action listener. Here is the code needed to respond to a users click.

    public void selectPanelStackPanel(ActionEvent action){
        if (panelStack != null){
            panelStack.setSelectedPanel(displayPanel);
        }
    }

The PanelStackBean object is introduced in this code block which is responsible for maintaining the state of selected panel stack in the panelStack component. The full source for this simple class is available with the tree-selection demo source code.

Download the demo:

Setting up a tree component for user interaction is a relatively simple process which utilizes action listener mechanisms in the JSF framework. Each tree node's backing bean is responsible for setting a selected panel in the panelStack component. The component nature of JSF allows for easy encapsulation of application logic which makes application quick to build and debug. The follow is a screen shot of the tree-selection demo.

Tree Selection Example

 

Customizing a Tree's Display

In preceding section we have seen how the tree component iterates over a DefaultTreeModel binding and applies a template to each node in the model. The template in previous examples used commandLinks but the template could have used any of the ICEfaces components. In this section we will apply the full Xp theme to a tree with multiple children.

Tree Style Example

The ICEfaces framework comes preconfigured with two distinctly different CSS based themes for all components. The style sheets for these two themes, Xp and Royale, are available in the resources folder of the ICEfaces bundle. In order apply the Xp theme the following include is needed in your jspx page:

    <link href="./xmlhttp/css/xp/xp.css" rel="stylesheet" type="text/css"/>

For most ICEfaces component added the above CSS file include is all that is needed. The tree component requires slightly more configuration to fully apply the theme. First, the tree component requires that that a imageDir attribute is set which points to a location where the expand and contract control images can be found. Lastly, the node state icons must be set for each IceUserObject; branch contracted icon, branch expanded icon and finally a leaf icon. This may seem like a lot of work but it allows for a tree that can have multiple variations of branch and leaf icons . The following is an updated tree component declaration which is fully styled:

    <ice:tree id="tree"
              value="#{tree.model}"
              var="item"
              hideRootNode="false"
              hideNavigation="false"
              imageDir="./xmlhttp/css/xp/css-images/">
        <ice:treeNode>
            <f:facet name="icon">
                <ice:panelGroup style="display: inline">
                     <h:graphicImage value="#{item.userObject.icon}"/>
                </ice:panelGroup>
            </f:facet>
            <f:facet name="content">
                <ice:panelGroup style="display: inline">
                    <ice:commandLink
                            actionListener="#{item.userObject.selectPanelStackPanel}"
                            value="#{item.userObject.text}"/>
                </ice:panelGroup>
            </f:facet>
        </ice:treeNode>
    </ice:tree>

Download the demo:

In summary, the ICEfaces components can easily styled using CSS and in some cases by specifying image directories like the tree component is necessary. In the next example we are going to dynamically change an tree's DefaultTreeModel which will automatically reflected in the view.


Dynamically Changing a Tree

In this next example we are going to dynamically remove and add nodes from the tree. When a user selects a tree node they will optionally have a choice to copy the selected node or remove it from the tree model. The following is a screen capture of this application.

Tree Style Example

This demo will start with the same tree that we used in the tree style demo. We must first add an action listener to the the treeNode commandLink component to listen for user clicks. Next we add a panel which will display the selected node and have add/remove controls when a node is selected. This JSF code for dynamically changing a tree node is as follows:

    <ice:panelGroup>
        <p>Dynamic Tree Node Control:</p>
        <ice:commandButton
            actionListener="#{tree.copySelectedNode}"
            disabled="#{tree.copyDisabled}" value="Copy" />

        <ice:commandButton
            actionListener="#{tree.deleteSelectedNode}"
            disabled="#{tree.deleteDisabled}" value="Delete" />
        <p/>
        <ice:outputText
            value="Selected Node: #{tree.selectedNodeObject.text}"
            escape="false" />
    </ice:panelGroup>

The ActionListeners for the add and remove node controls can be added to the TreeBean class. When a node in the tree is clicked an instance variable in TreeBean references the source of the click. The reference to the selected node can them be used to copy or remove the selected node from the DefaultTreeModel when one of the corresponding commandButtons is pressed. Note that the root node is treated as a special case, if it is removed no more nodes can be added to the tree.

    public void deleteSelectedNode(ActionEvent event){
        if (selectedNodeObject != null && !selectedNode.equals(ROOT_NODE_TEXT)){
            selectedNodeObject.deleteNode(event);
            selectedNodeObject = null;
        }
     }

    public void copySelectedNode(ActionEvent event){
            if (selectedNodeObject != null)
                selectedNodeObject.copyNode(event);
    }

Download the demo:

 

Examples that Use Trees

Example Source Notes
Component Showcase Available in release bundles Tree example where a tree that responds to user selection. It also has controls for removing and adding nodes. Navigation tree which manipulates a panel stack selected item.
Webmail Available in release bundles Tree component used for navigation. Nodes are dynamically created depending on the number of mail accounts and subsequent child mail folders.
tree-basic tree-basic source code Simple example of how to setup a basic tree component and backing bean
tree-links tree-links source code Tree component which has commandLink components as nodes. When a node is clicked a new browser window is launched with the respective URL.
tree-selection tree-selection source code Tree component is used to manipulate the selected panel in a panel stack.
tree-style tree-style source code The XP theme is fully applied to a tree component producing a fully styled tree.
tree-dynamic tree-dynamic source code A tree components default tree model is manipulated by other Java Beans. This applicaiton shows how the Tree component can be dynamically changed.

The ICEfaces Tutorial
Table of Contents

Copyright 2006 ICEsoft Technologies Inc. All rights reserved.