How to Use the ICEfaces Tree ComponentThe 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. 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 toggled by setting the tree hideRootNode attribute to false. Each DefaultMutableTreeNode wraps an IceUserObject which contains instance variables for common tree 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
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 to bind it to the tree components' value attribute. TreeBean.java // 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); } The TreeBean must be instantiated in faces-config.xml so it can be used 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 facet named content. The facet name is self descriptive and allows for any type of JSF component to be added. 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 DefaultMutableTreeNodes found in the DefaultTreeModel. To summarize, 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
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 linkTree.xhtml. The following code shows how to add a commandLink to a 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> Customising the IceUserObject is quite simple and when combined with the iterative nature of the tree component, it can be quite powerful. The next section will show how to respond to node selection.
Responding to Node Selection
PanelSelectUserObject.java 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 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 user's click. PanelSelectUserObject.java 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. Setting up a tree component for user interaction is a relatively simple process which utilizes actionListener 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 the application quick to build and debug. The follow is a screen shot of the tree-selection demo.
Customizing a Tree's Display
The ICEfaces framework comes configured with three distinctly different CSS based themes for all components. The style sheets for these themes, XP, Royale and Rime are available in the resources folder of the ICEfaces bundle. In order apply the XP theme the following include is needed in your xhtml page:
<link href="./xmlhttp/css/xp/xp.css" rel="stylesheet" type="text/css"/>
For most ICEfaces components 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 an 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> In summary, the ICEfaces components can easily be styled using CSS and in some cases by specifying image directories as we have done with our tree. In the next example we are going to dynamically change a tree's DefaultTreeModel which will automatically be reflected in the view.
Dynamically Changing a Tree
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. The 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. TreeBean.java 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); }
Tutorial Source Code Downloads
|
ICE Tree Tutorial
© Copyright 2021 ICEsoft Technologies Canada Corp.