A JSF project that I've been working on required a dynamic tree. Not just a tree that loads dynamically, but a tree that a user can perform CRUD operations on and instantly see the changes they've made. I'm going to walk through an example of how to accomplish this.
The basic setup:
- A "'tree" structure that is made up of "node" objects. Each node has a parent node as well as a list of child nodes. Root nodes have a null parent. The tree object takes a list of root nodes. (see code below)
- A JSF Tree. I used the RichFaces tree because RichFaces was already being used throughout the application, but this can be applied to other JSF trees as well. (see code below)
- A JSF managed backing bean with ActionListeners and DAO access. ActionListeners are used for the CRUD operations on nodes. DAO access is used for directly updating the objects in the database. (see code below)
The node:
import javax.persistence.*;
import java.util.List;
import org.hibernate.annotations.GenericGenerator;
@Entity
@GenericGenerator(
name = "hibernate-uuid", strategy = "uuid"
)
public class Node {
@Id
@GeneratedValue(generator = "hibernate-uuid")
private String id;
@Column(nullable = false)
private String label;
@OneToMany(mappedBy = "parent", cascade = {CascadeType.ALL})
private List children;
@ManyToOne
@JoinColumn(name = "fk_parent_id")
private Node parent;
// Getters and Setters...
}
The tree:
<rich:tree id="tree" ajaxSubmitSelection="true" switchType="client" onselected="return false;">
<rich:recursiveTreeNodesAdaptor var="node" roots="#{treeCreator.rootNodes}" nodes="#{node.getChildren}">
<rich:treeNode data="#{node.id}">
<!--Node Name-->
<h:outputText value="#{node.label}" />
<!-- Control Buttons -->
<!--Button used to toggle addNode Div -->
<input type="image" value="Add Child" title="Add Child"
onclick="toggleLayer('addNode#{node.id}');return false;"
src="resources/images/controls_addlevel.gif"/>
<!--Button used to toggle editNode Div -->
<input type="image" value="Edit Node" title="Edit Node"
onclick="toggleLayer('editNode#{node.id}');return false;"
src="resources/images/controls_edit.gif"/>
<!--Button for Deleting node -->
<a4j:commandButton actionListener="#{treeCreator.deleteNode}"
data="#{node.id}" value="Delete Node" reRender="tree"
image="/resources/images/controls_delete.gif"
title="Delete Node"
onclick="if(!confirm('Delete Node?')){ return; };"/>
<!-- Divs for name entry/edit-->
<div id="addNode#{node.id}" style="display:none;">
<h:outputText value="Node Name: "/>
<h:inputText binding="#{treeCreator.nodeAdd}"/>
<a4j:commandButton value="Save and Create" title="Save and Create"
actionListener="#{treeCreator.addNode}"
data="#{node.id}"
onclick="toggleLayer('addNode#{node.id}');"
image="/resources/images/icon_small_save.gif"
reRender="tree"/>
<input type="image" src="resources/images/icon_small_cancel.gif" value="Cancel"
onclick="toggleLayer('addNode#{node.id}');return false;" title="Cancel"/>
</div>
<!--Div for Editing an Org -->
<div id="editNode#{node.id}" style="display:none;">
<h:outputText value="New Node Name: "/>
<h:inputText binding="#{treeCreator.editNode}"/>
<a4j:commandButton value="Save Changes" title="Save Changes"
actionListener="#{treeCreator.editNode}"
data="#{node.id}"
onclick="toggleLayer('editNode#{node.id}');"
image="/resources/images/icon_small_save.gif"
reRender="tree"/>
<input type="image" src="resources/images/icon_small_cancel.gif" value="Cancel"
onclick="toggleLayer('editNode#{node.id}');return false;" title="Cancel"/>
</div>
</rich:treeNode>
</rich:recursiveTreeNodesAdaptor>
</rich:tree>
*Note that toggleLayer() is a javascript function that hides/unhides the contents of a given div tag.
*Note that the inputText fields have bindings to the backing bean.
The ActionListeners:
public void addNode(ActionEvent event) {
//get the button that fired the event
HtmlAjaxCommandButton button = (HtmlAjaxCommandButton) event.getComponent();
//get ID of the parent Node - it's stored in the data attribute of the add button
String parentNodeId = (String) button.getData();
//get parent node object
Node parentNode = nodeDao.getById(parentNodeId);
//create new node object and set label, parent, children, etc
Node nodeToAdd = new Node();
nodeToAdd.setLabel((String) addNode.getValue());
nodeToAdd.setParent(parentNode);
nodeToAdd.setChildren(new HashSet());
parentNode.getChildren().add(nodeToAdd);
//save node to DB
nodeDao.saveOrUpdate(parentNode);
}
public void editNode(ActionEvent event) {
//get the button that fired the event
HtmlAjaxCommandButton button = (HtmlAjaxCommandButton) event.getComponent();
//get ID of Node to edit - it's stored in the data attribute of the edit button
String nodeId = (String) button.getData();
//load node from DB
Node nodeToEdit = nodeDao.getById(nodeId);
//update node label from UI input
nodeToEdit.setLabel((String) editNode.getValue());
//update node label in DB
nodeDao.update(nodeToEdit);
}
public void deleteNode(ActionEvent event) {
//get ID of Node to delete - it's stored in the data attribute of the delete button
HtmlAjaxCommandButton button = (HtmlAjaxCommandButton) event.getComponent();
String nodeId = (String) button.getData();
//get node object via Dao
Node nodeToDelete = nodeDao.getById(nodeId);
//set reference from parent to null
Node parentNode = nodeToDelete.getParent();
//don't delete the Root Node
if (parentNode != null) {
Set childNodes = parentNode.getChildren();
childNodes.remove(nodeToDelete);
//put updated list of childNodes on parentNode
parentNode.setChildren(childNodes);
//save parentNode to DB
nodeDao.save(parentNode);
//set parent to null
nodeToDelete.setParent(null);
//delete the node from the DB
nodeDao.delete(nodeToDelete);
}
}
The managed bean within faces config:
<managed-bean>
<description>
Backing bean for the tree
</description>
<managed-bean-name>treeCreator</managed-bean-name>
<managed-bean-class>path.to.the.TreeCreator</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>nodeDao</property-name>
<value>#{nodeDao}</value>
</managed-property>
</managed-bean>
Here's how it all fits together using the Ã'Add ChildÃ" operation as an example:
- A user visits the tree page and sees the tree with a single root node:
- The user clicks the Ã'Add ChildÃ" button on the root node:
-
The Ã'addNode
Ã" div is unhidden (via javascript) which displays an input text box, a save button, and a cancel button to the right of the root node. - The user types in a name for the node and clicks the save button. Note that the save button has a few attributes including actionListener=Ã"treeCreator.addNodeÃ" and data=Ã"#{node.id}Ã".
-
The Ã'addNode
Ã" div is hidden again (via javascript). - The addNode ActionListener is called. See the addNode ActionListener.
- The save button is retrieved from the ActionEvent that was passed to this ActionListener. The Ã'dataÃ" attribute stored on this save button is retrieved which contains the ID of the soon-to-be-parent node. The parent node object is loaded via dao access. A new node (with user specified name which is retrieved via binding of the inputText box to backing beans) is added to the parent's children list. The parent node is saved which also saves the new child object to the database.
- The tree is refreshed and the new node appears. Note that the tree "switchType" can be set to client, server, or ajax. This will effect how the page refreshes and the tree loads.
Edit and delete follow a similar process with slightly different implementations in their ActionListeners.
14 Comments
Leave a comment
0 TrackBacks
Listed below are links to blogs that reference this entry: Dynamic Trees in JSF.
TrackBack URL for this entry: http://www.nearinfinity.com/mt/mt-tb.cgi/421



Please, help, the TreeCreator.java class is missing. Could you show the code of this class?
Thank you very much.
Maja,
Take a look at the code below. You will need to pass buildTree a list of root nodes. If there is only 1 root node, pass a list with 1 item in it.
public Node buildTree( List nodes ) {
List tree = new ArrayList(nodes.size());
for ( Node node : nodes ) {
if ( node.getChildren() != null ) {
List children = buildTree( node.getChildren() );
}
tree.add( node );
}
return tree;
}
First of all thanks for the information.This is what i have been searching for a long time.
It would be great if u Can post treeCreator.java . thanks in advance.
Hi,
With regards to the rich:recursiveTreeNodesAdaptor.
I've read the documentation on it but do not know
the terminating condition for it ?
Does getting a null from #{node.getChildren} terminate generating of more branches ?
And also I would too be grateful to have the complete set of source codes to study.
Best regards,
Joe.
Joe,
If a node has no children, node.getChildren() will return null. You don't need to set any specific terminating condition. The recursiveTreeNodesAdaptor will figure that out for you.
Hope this helps,
Michael Bevels
Hi Michael,
I«m trying to use the rich:tree with recursive. But the tree show only 2 levels when my data have more than this. I think that my tree is not working recursively.
In my test I use a recursive hibernate entity too, and debuging I can see that all level were loaded.
I have some doubts:
-If entity is recursively, why you need the method 'buildTree( List nodes )'?
- One thing that yout code is different from mine, is that you use #{node.getChildren} and I use #{node.filhos} (filhos is children in portuguese). #{node.getChildren} launch an error for me, saying that the getchildren property not exists.
Thank you very much.
DŽcio
Decio,
The method buildTree is used to build the tree of Nodes. A Node contains a list of Nodes (children). This data structure needs to be built so it can be passed to the rich:tree component.
The code I provided isn't complete. The "//getters and setters" comment in the code implied that the fields in the Node class need get and set methods.
Rich:tree should be passed a list of root node. The rich:tree component then builds the tree based on the tree/Node data structures built on the backend.
Hope this helps,
Michael Bevels
Michael,
I was looking for tree that loads dynamically with node text read from database. Any helper code for this. This topic with dynamic tree with CRUD operations is very well explained any blog for tree that loads dynamically? I would really appreciate any help.
Thanks,
michael,
first for your information but i have a need i would be very happy if u can post your TreeCrator.java class please
thanks in davance
Hi micheal,
Would u please share javascript function toggleLayer() code. What is the meaning of passing parameter, to this function as,('addNode#{node.id}'). What is 'addNode' means here? Please explain this!!!! :)
Hi micheal,
Sorry, for the last comment. Now, I noticed its a div name you are sending and you are generating ids dynamically. What really surprises me, is that, when i tried to sent EL expression value, to Javascript, it shows me the error. Do you have any idea on this??
Can you provide more information on the error you're seeing?
hi, thi is raghu. we are using (moreSearch is a method to get the more results under the same node)
how can i achieve this
pls help me out. its very urgent
Hello, it's nice post and very useful, i think that i do the same thing but i haven't a big exeprience with Richfaces, i would like to do a dynamic menu with a tree technologie, but I don't know the depth of menu items, can-you help me provide the source of this example ? thinks.