Saturday, 18 April 2015

Interactions with RichTree

Possible cases

Interacting with the tree may have many faces. You e.g. might need to:

- react to clicking on tree nodes,
- traversing the tree by expanding nodes,
- selecting / multiselecting tree nodes.

Also you might need to change the tree state by invoking external actions, e.g. clicking the external button to:

- expand relevant tree path,
- change tree's selection.


Working example ilustrating the use case

Let's think of following application (run example). I provide functionality of traversing the tree with two external buttons (it's a wizard like navigation - Next - Previous - relation). I also provide expansion of tree node by clicking a button (I've hard-coded 'Web containers' node to be expanded).

How does it work

To implement the tree I used Java Server Faces 2.1 web MVC with RichFaces 4.5.4 <rich:tree> component. On the xhtml side I configured it in the following way:

<rich:tree id="tree"
    selectionType="ajax"
    value="#{webTechnologiesTreeBean.rootNode}"
    var="node" 
    selection="#{webTechnologiesTreeBean.selection}"
    selectionChangeListener="#{webTechnologiesTreeBean.processTreeSelectionChange}">
    <rich:treeNode expanded="#{node.expanded}" iconLeaf="images/mobilne.jpg">
        <h:outputText value="#{node.data.name}"/>
    </rich:treeNode>
</rich:tree>
On the managed bean side i've bound component properties in the following way (selection to WebTechnologiesTreeBean) :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
 @ManagedBean
 @SessionScoped
 public class WebTechnologiesTreeBean implements Serializable, ITree {

     private Collection<Object> selection = new ArrayList<Object>()

     @Override
     public Collection<Object> getSelection() {
         return selection;
     }

     public void setSelection(Collection<Object> selection) {
         this.selection = selection;
     }

     (..)

 }   

... and expansion to WebTechnologiesTreeNode class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 public class WebTechnologiesTreeNode extends TreeNodeImpl implements Serializable{

     private boolean expanded = false;
    
     public boolean isExpanded() {
         return expanded;
     }

     public void setExpanded(boolean expanded) {
         this.expanded = expanded;
     }

     (..)
 }

The whole tree model has been built using Builder design pattern implemented inside WebTechnologiesTreeNode. This building process includes passing data to nodes and node keys which are node's indentifiers within a tree. Leaf node's keys are auto-numerated with "1", "2" etc.. Using this String typed keys i am able to retrieve relevant node later on.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
private WebTechnologiesTreeNode createRootNode() {
    WebTechnologiesTreeNode result = new WebTechnologiesTreeNode.NodeBuilder().build();
    WebTechnologiesTreeNode frameworkNode = new WebTechnologiesTreeNode.NodeBuilder()
            .withData(new SimpleNodeData("Web frameworks"))
            .withChildLeaf(new WebLinkNodeData("Java server faces", "https://javaserverfaces.java.net/"))
            .withChildLeaf(new WebLinkNodeData("Vaadin", "https://vaadin.com/home"))
            .withChildLeaf(new WebLinkNodeData("Spring MVC", "http://projects.spring.io/spring-framework/"))
            .withChildLeaf(new WebLinkNodeData("Play", "https://www.playframework.com/"))
            .withChildLeaf(new WebLinkNodeData("Struts", "https://struts.apache.org/"))
            .build();
    WebTechnologiesTreeNode containerNode = new WebTechnologiesTreeNode.NodeBuilder()
            .withData(new SimpleNodeData("Web containers"))
            .withChildLeaf(new WebLinkNodeData("Tomcat", "http://tomcat.apache.org/"))
            .withChildLeaf(new WebLinkNodeData("Glassfish", "https://glassfish.java.net/"))
            .withChildLeaf(new WebLinkNodeData("JBoss", "http://www.jboss.org/"))
            .build();
    result.addChild("frameworks", frameworkNode);
    result.addChild("containers", containerNode);
    return result;
}

Handling expansion programatically (server-side)

Now, when I've got data model, property bindings I can collapse / expand my hard-coded node with key 'containers' (display name 'Web containers').

1
2
3
4
5
 public void switchContainersExpansion() {
     WebTechnologiesTreeNode node = 
         (WebTechnologiesTreeNode) rootNode.getChild("containers");
     node.setExpanded(!node.isExpanded());
 }

Handling selection on server side

In RichFaces 4.5.X selection is kept with List<SequenceRowKey> each SequenceRowKey instance has a path consisted of tree node string keys. For example, in my demo app, path to 'Java Server Faces' tree node would be SequenceRowKey["frameworks", "0"], path to 'JBoss' node would be SequenceRowKey["containers", "2"]

To programatically set single selection on <rich:tree> I have to set one element list in bound selection property. Let's say I want to set 'JBoss' selected:
1
2
3
 Collection<Object> mySelection = new ArrayList();
 mySelection.add(new SequenceRowKey("containers", "2"));
 treeBean.setSelection(mySelection);
Please feel free to review source code of my example application and see GitHub repository.