Friday, May 29, 2009

RichFaces tree - dynamic update of tree model

The previous RidhFaces simple tree example allows you to render tree data which are output at one render time. But what if your tree data model is so large, so that you can't render it all at once. Instead you have to update your tree model at run time on user request?

Rich faces provides a listener called change expand listener. In case the user selects certain node in your tree, this listener will be called and here you can update your tree model. In the following section I will show how this is done. Let's first see how this listener is bound to a tree component:








Please notice that I left all other tree property's intentionally. As you can see, this property will call the assigned method where you can handle your tree model. Let us look into the method closely and see how we can update our tree model.


import org.richfaces.event.NodeExpandedEvent;

public class MyBackingBean{

/**
* The tree expand expand listener method.
*/
public void processExpansion(NodeExpandedEvent nodeExpandedEvent){
// get the source or the component who fired this event.
Object source = nodeExpandedEvent.getSource();
if (source instanceof HtmlTreeNode) {
// It should be a html tree node, if yes get
// the ui tree which contains this node.
UITree tree = ((HtmlTreeNode) source).getUITree();
// avoid null pointer exceptions even though not needed. but safety first ;-)
if (tree == null) {
return;
}
// get the row key i.e. id of the given node.
Object rowKey = tree.getRowKey();
// get the model node of this node.
TreeNode selectedTreeModelNode = tree.getModelTreeNode(rowKey);
if(null != selectedTreeModelNode){
// add the children nodes.
addChildrenNodes(selectedTreeModelNode);
}
}
}
/**
* Method for adding children nodes to a given tree model node.
*/
public void addChildrenNodes(TreeNode node){
//process your node somehow. For this example I add only one node.
TreeNodeImpl newNode = new TreeNodeImpl();
newNode.setData("My New Node");
newNode.setParent(node);
node.addChild("rowKey", newNode);
}
}


So every time user clicks the icon for expanding nodes, this listener will be called and the tree model will be updated. When the response will be rendered, the updated tree model will be rendered out, including your newly added node(s).

The same process can be done while the user selects particular node of the tree. When the user selects particular node, a RichFaces will look for queued Select node listeners to be called. Such listener can be registered is a Select Node Listener.







To queue select node listener to the tree, you can use the nodeSelectListener property. Than you can create new public listener method in your backing bean as follow:


import org.richfaces.event.NodeSelectedEvent;

public class MyBackingBean{

/**
* The select node listener method.
*/
public void selectNode(NodeSelectedEvent event){
// get the component that fired this event.
UIComponent component = event.getComponent();
if (component instanceof HtmlTree) {
// if a rich faces html tree (it is sometimes also a ui tree)
//get the row key of selected node.
Object rowKey = ((HtmlTree) component).getRowKey();
//pull the model tree node out of ther.
TreeNode selectedTreeModelNode = ((HtmlTree) component).getModelTreeNode(rowKey);
if(null != selectedTreeModelNode){
// add the children to the model.
addChildrenNodes(selectedTreeModelNode);
}
}
}
}


You should notice that the same method for adding children to the model is called as in previous example for expanding the node. Now that we covered how we can update our tree model, we could decide that after selecting a node and updating it's model we would like to expand the node. To do this, there are few way's. The safest and preferable way would be through the tree state saving property. Let's look into it closer:








In the backing bean, you should create new property called tree state. This should look something like this:



/**
* Getters and Setters added.
*/
public class MyBackingBean{

private org.richfaces.component.state.TreeState treeState;

public void setTreeState(org.richfaces.component.state.TreeState treeState) {
this.treeState = treeState;
}

public org.richfaces.component.state.TreeState getTreeState() {
return treeState;
}

}


Now that we added the Getters and Setters for this property, we could add this line to our select node listener method:



import org.richfaces.event.NodeSelectedEvent;

public class MyBackingBean{

public void selectNode(NodeSelectedEvent event) throws Exception {

// ... same as in the previous section
if(null != selectedTreeModelNode){
//.... as in previous section
// queue the node to be expanded.
getTreeState().expandNode((UITree) tree,
(TreeRowKey) rowKey);
}
}
}
}


The tree state instance will be consulted at render time of the tree. It contains map's where the state of the nodes is saved. Expanded nodes have own map where the row key's are queued for expanded rendering. The method expand node of the tree state class queue's the row key of the given node to be rendered as expanded.

As you see, RichFaces is very flexible and provides methods for dynamic extending of the tree structure, avoiding to render the whole content of the tree at the beginning.

Pity that they don't have good tutorials on their components.

Thursday, May 28, 2009

Richfaces Tree - simple case

RichFaces Tree basics

RichFaces framework is providing a convenient approach for rendering trees in a browser. Information on the RichFaces tree component are found here.

There are few steps to be taken, when implementing a tree. These are:

  1. Creating your tree model class, which will hold the data that should be rendered by the tree.
  2. You have to include the RichFaces tree tag in your form, weather JSP or XHTML.
  3. Bind your tree model class with the tree tag using the JSF Expression language.
This tree steps are not that unfamiliar to any JSF developer. So let's look closely to them.

Let us start with step one and discuss the model class. Good way to start coding your model class is to extend the org.richfaces.model.TreeNode Interface provided by RichFaces. This interface is implementing all important methods needed by the tree to render your data. Another convinient way of providing your model for a tree is to use the RichFaces implementation org.richfaces.model.TreeNodeImpl of the above mentioned interface.

This is really the basic and most likely all that one need for a simple RichFaces tree.

Let us examine how a tree can be implemented as a set of objects!?

As a entry point we need a root node. Each root node have this attributes:
  1. Root node has no parent.
  2. Root node has one or many children.
  3. Root node has a name and id.
  4. Root node must not have a name.
  5. Root node is also a folder.
So for construction our tree model , we need a root node. This can be done by instantiating the TreeNodeImpl class this way:



public class TreeModel {
private org.richfaces.model.TreeNodeImpl root;

// GETTER and SETTER

//Constructor
public TreeModel(){
root = new org.richfaces.model.TreeNodeImpl();
root.setParent(null);
root.setData("Name of my Root Node");
}

}


That ain't that hard, don't you think?
Now that we have the root node, we can fill it with it's content or children nodes this way:


public class TreeModel{

public List treeContent;
public void propagateTree(){

for(Iterator it = treeContent.iterator(); it.hasNext();){
TreeNodeImpl node = new TreeNodeImpl();
NameIdPair pair = (NameIdPair) it.next();
node.setData(pair.getName());
node.setParent(root);
root.addChild(pair.getId(),node);
}
}
}


The class NameIdPair is only a data object contain two fields, name and id and it's getter and setter methods. The data in the list can be retrieved from a data base or any content management system.

In this case, the root node will become will become one level nodes, dependent on how many entries were saved in the list.

Now that we have our simple model, we need a tree that will render it.

The following is convenient way how to use the tree tag:


rerender="selectedNode"
ajaxsubmitselection="true" switchtype="ajax"
treenodevar="TreeNodeVar"
value="#{treeDataProvider.rootNode}"
var="treeItem" ajaxkeys="#{null}"
nodeface="node">

iconleaf="/icons/folder.png" icon="/icons/folder.png">





As you can see, the tag is very simple. The following points are worth mentioning:
  1. The root node we propagated and filled with other nodes is bound to the value property of the tree. The tree tag will dynamically load the root node and will render it and it's content.
  2. RichFaces tree tag provides three operating modes of rendering: server, ajax, client. The most convenient way is the ajax mode, which will update the tree with ajax and let you select a node with the help of ajax. The client mode is only good for small trees as the one in our example. It will render the whole content with java script to your brower. If you choice is the server mode, you will have to find a way how to pos a request to the server, so that your selection or model update take place.