In the wake of my last post – Getting started with Java Server Faces (JSF) using Apache MyFaces – in JDeveloper 10.1.3EA
– I will continue my explorations of MyFaces in this article. In
particular, I will take a look at the Tree2 Component that is shipped
with Apache MyFaces. I will make use of Oracle JDeveloper 10.1.3 EA as
my IDE. For details on setting up Apache MyFaces with JDeveloper
10.1.3, see the post I just mentioned. To make things easier for me –
and hopefully for some of you as well – I have created a
starter-project, a JDeveloper 10.1.3 Application Workspace with
MyFacesBase project that contains the files you need to get started
with MyFaces. You can simply download this zipfile: MyFacesBase.zip,
extract it on your PC and open the JWS file in JDeveloper 10.1.3. You
still need to set up the project with proper library definitions – see
the previous post for details.
Starting from this MyFacesBase Project we will create a beautiful tree-based JSF application. Sort of.
Steps to prepare
I will first gear the base environment to my specific Tree oriented project:
- Extract MyFacesBase.zip to a directory; let’s refer to that directory as MyFacesTree_HOME
- Change
the string Base to Tree in the names of the subdirectories
MyFacesTree_HOME\MyFacesBase and
MyFacesTree_HOME\MyFacesBase\MyFacesBase - Open JDeveloper 10.1.3
- Load the file MyFacesBase.jws; this will open the MyFacesBase application workspace
- Rename the Application to MyFacesTree.
- Change
the name of the MyFacesBaseProject to MyFacesTreeProject. If you do not
already see the project, then add it to the Application Workspace by
adding the fiule MyFacesBaseProject.jpr. - Go to the Project
Properties of the MyFacesTree project. Verify in the Libraries tab
whether the libraries MyFaces and JSTL are set up correctly and added
to the project. If necessary see the post mentioned above. Also check
whether the JSP Tag Libraries MyFaces-Core, MyFaces-HTML and
MyFaces-ext (or Tomahawk) have been added to the project.
Verifying the preparations
Now
is a good time to make sure that we can indeed create and run JSF
applications in the JDeveloper 10.1.3 environment and project we just
set up. Follow the these simple steps:
- From the Right Mouse
Button Menu on the MyFacesTreeProject project, go to the New Gallery.
Select the node Web Tier, JSF and select the option JSF JSP. Walk
through the Wizard, accepting all default choices – or making
refinements as you like. - The wizard create a JSP with basic JSF
layout – importing the required Tag Libraries and layout a
<f:view> and a <f:form>. The JSP is opened in the editor.
Type a few characters – just to have some output to look at in our
browser. Note: at this point we have not made any changes to
faces-config.xml or any other configuration file. - From the RMB
menu on the JSP, choose the option run. The “application” should now
open in the browser, displaying your trivial output.
If you have gotten this far, you are in good shape to start using the MyFaces Tree component.
Developing with the MyFaces Tree2 Component
Apache
MyFaces is a valuable open source project providing the base
implementation of the JSF standard and extending it with a large set of
advanced components, called Tomahawk. The set includes TabbedPane,
RssReader, Advanced Table features, inputDate and Calendar, HtmlEditor,
Pulldown Menu, and many others. To confuse matters, there is a Tree, a
Tree2 and a Tree Table component. It seems that Tree2 effectively
replaces Tree; Tree is labeled ‘sort of obsolete’. So I focus on Tree2
for now.
Using the JSP JSF wizard from the New Gallery I create
a new JSP called LibraryTree.jsp. From the Component Palette, we drag
and drop the Tree2 component to the JSP. The page source looks like:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ page contentType="text/html;charset=windows-1252"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://myfaces.apache.org/extensions" prefix="t"%> <f:view> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/> <title>LibraryTree</title> </head> <body><h:form> <t:tree2 id="serverTree" value="#{LibraryTreeHandler.treeModel.treeData}" var="node" varNodeToggler="t" clientSideToggle="false"> <f:facet name="book"> <h:panelGroup> <t:graphicImage value="/images/yellow-folder-open.png" rendered="#{t.nodeExpanded}" border="0"/> <t:graphicImage value="/images/yellow-folder-closed.png" rendered="#{!t.nodeExpanded}" border="0"/> <h:outputText value="Book: " styleClass="nodeFolder"/> <h:outputText value="#{node.description}" styleClass="nodeFolder"/> </h:panelGroup> </f:facet> <f:facet name="author"> <h:panelGroup> <t:graphicImage value="/images/yellow-folder-open.png" rendered="#{t.nodeExpanded}" border="0"/> <t:graphicImage value="/images/yellow-folder-closed.png" rendered="#{!t.nodeExpanded}" border="0"/> <h:outputText value="#{node.description}"/> <h:outputText value=" (Author)" styleClass="nodeFolder"/> </h:panelGroup> </f:facet> <f:facet name="foo-folder"> <h:panelGroup> <t:graphicImage value="/images/yellow-folder-open.png" rendered="#{t.nodeExpanded}" border="0"/> <t:graphicImage value="/images/yellow-folder-closed.png" rendered="#{!t.nodeExpanded}" border="0"/> <h:outputText value="#{node.description}" styleClass="nodeFolder"/> <h:outputText value=" (#{node.childCount})" styleClass="childCount" rendered="#{!empty node.children}"/> </h:panelGroup> </f:facet> </t:tree2> </h:form></body> </html> </f:view>
There
are a few things worth noting here. First of all, the tree2 components
has child facets; there is a facet for each type of node that the tree
will contain. Apparently our tree wil contain nodes of types
foo-folder, author and book. We will see later how we specify the node
type on the backing bean’s model data. Each facet describes how the
node type must be displayed; various node-types can make use of
different icons, styles, actions etc. Note how the Node Interface
provides properties like childCount, nodeExpanded and nodeSelected.
The
value for the tree2 component is set to:
value=”#{LibraryTreeHandler.treeModel.treeData}”. That suggests that
there is a Managed Bean called LibraryTreeHandler that exposes a
getTreeModel method that returns an object that has a method
getTreeData(). This last method must return an object that implements
the TreeNode interface – in package org.apache.myfaces.custom.tree2.
So there we go: create the required objects. First the LibraryHandler class:
package nl.amis.myfaces.tree; public class LibraryHandler { private LibraryTreeModel treeModel; public LibraryHandler() { treeModel = new LibraryTreeModel(); } public LibraryTreeModel getTreeModel() { return treeModel; } }
This
class is a used for the managed bean called LibraryTreeHandler and
referenced by the value property of the tree2 component. See the
faces-config.xml file:
<?xml version="1.0" encoding="windows-1252"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config xmlns="http://java.sun.com/JSF/Configuration"> <managed-bean> <managed-bean-name>LibraryTreeHandler</managed-bean-name> <managed-bean-class>nl.amis.myfaces.tree.LibraryHandler</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config>
The LibraryHandler class relies on the LibraryTreeModel class. Its code is as follows:
package nl.amis.myfaces.tree; import org.apache.myfaces.custom.tree2.TreeModel; import org.apache.myfaces.custom.tree2.TreeNode; import org.apache.myfaces.custom.tree2.TreeNodeBase; public class LibraryTreeModel { public LibraryTreeModel() { } public TreeNode getTreeData() { TreeNode treeData = new TreeNodeBase("foo-folder", "Library", false); TreeNodeBase authorNode = new TreeNodeBase("author", "Ben Elton", false); TreeNodeBase bookNode = new TreeNodeBase("book", "Post Mortem", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Blast from the Past", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Stark", false); authorNode.getChildren().add(bookNode); treeData.getChildren().add(authorNode); authorNode = new TreeNodeBase("author", "Orson Scott Card", false); bookNode = new TreeNodeBase("book", "Ender's Game", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Xenocide", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Shadow Puppets", false); authorNode.getChildren().add(bookNode); treeData.getChildren().add(authorNode); authorNode = new TreeNodeBase("author", "Rod Johnson", false); bookNode = new TreeNodeBase("book", "J2EE Design and Development", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "J2EE without Enterprise Java Beans", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Professional Java Development With The Spring Framework", false); authorNode.getChildren().add(bookNode); treeData.getChildren().add(authorNode); return treeData; } }
The
application looks like this in the browser. We can expand and collapse
nodes. Note that all tree manipulation is done server side: each node
expansion requires browser-to-server communication for updating the
treenodes.
The Project with the application so far can be downloaded here: MyFacesTree_Stage1.zip
Make the LibraryTree application a little more interesting
We
are now going to make the application a little more interesting. We
will allow Author nodes to be selected and we will display the
Biography of the currently selected author – marked in the tree with an
asterisk – next to the tree. It is remarkable how simple it is to add
these changes to the application.
If
we select an Author node in the tree, the tree is redrawn and an
asterisk is placed in front of that node – in the screenshot the
selected node is for Orson Scott Card. At the same time, when the page
is refreshed, the Results of the currently selected author are updated
with the biography for the currently selected author.
The changes I had to make to realize this functionality are in three location:
1. First the JSP LibraryTree.jsp
I have added the asterisk for the currently selected Author node:
<h:outputText value="*" rendered="#{t.nodeSelected}"/>
You can see how simple it is: the asterisk is only rendered if the current node in the tree is selected.
To make the author node selectable, we add a commandLink:
<h:commandLink action="nodeClicked" actionListener="#{LibraryTreeHandler.processAction}" value="#{node.description}" immediate="true"> </h:commandLink>
When
the node is clicked on, the processAction method in the
LibraryTreeHandler bean is invoked; this method needs to have a return
type of void and accept a single ActionEvent input parameter.
The next change is displaying the biography for the current author:
<h:panelGroup rendered="#{LibraryTreeHandler.authorBio!= null }"> <f:verbatim> <h3>Results of the currently selected author</h3> </f:verbatim> <h:outputText style="font-size:70%; font-family:arial" value="#{LibraryTreeHandler.authorBio}"/> </h:panelGroup>
I
used a panelGroup for the rendered property: the entire panelGroup is
only displayed if there is indeed a Biography available. Note that the
bio is read from the authorBio property in the LibraryTreeHandler bean
that is managed based on the LibraryHandler class. We will see that
class in a moment.
I finally made use of a panelGrid to display
the tree and the bio next to each other. A panelGrid element in HTML is
typically rendered as table. Okay, it is not pretty but it will do the
trick.
2. The LibraryHandler class
This class now suddenly needs to provide an authorBio property and it has to absorb the processAction event.
package nl.amis.myfaces.tree; import javax.faces.component.UIComponent; import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import org.apache.myfaces.custom.tree2.HtmlTree; import org.apache.myfaces.custom.tree2.TreeNodeBase; public class LibraryHandler { private LibraryTreeModel treeModel; private String authorBio; public LibraryHandler() { treeModel = new LibraryTreeModel(); } public LibraryTreeModel getTreeModel() { return treeModel; } public void setAuthorBio(String authorBio) { this.authorBio = authorBio; } public String getAuthorBio() { return authorBio; } public void processAction(ActionEvent event) throws AbortProcessingException { UIComponent component = (UIComponent)event.getSource(); while (!(component != null && component instanceof HtmlTree)) { component = component.getParent(); } if (component != null) { HtmlTree tree = (HtmlTree)component; TreeNodeBase node = (TreeNodeBase)tree.getNode(); tree.setNodeSelected(event); setAuthorBio(((ExtendedTreeNode)node).getLongDescription()); } } }
You can see that we make use of a new class, the ExtendedTreeNode. So we also need to create that class:
3. The new ExtendedTreeNode class
In order to have the new biography property for our Authors, we introduce this class:
package nl.amis.myfaces.library; import org.apache.myfaces.custom.tree2.TreeNode; import org.apache.myfaces.custom.tree2.TreeNodeBase; public class ExtendedTreeNode extends TreeNodeBase { private String longDescription; public ExtendedTreeNode( String type, String description, boolean p3, String longDescription) { super(type, description, p3); this.longDescription = longDescription; } public void setLongDescription(String longDescription) { this.longDescription = longDescription; } public String getLongDescription() { return longDescription; } }
In
class LibraryTreeModel we use this class to populate the nodes of our
tree. The nodes we create for Authors are of the new ExtendedTreeNode
type that include the biography. So we modify the LibraryTreeModel.java
to:
package nl.amis.myfaces.tree; import org.apache.myfaces.custom.tree2.TreeNode; import org.apache.myfaces.custom.tree2.TreeNodeBase; public class LibraryTreeModel { public LibraryTreeModel() { } public TreeNode getTreeData() { String bio = null; TreeNode treeData = new TreeNodeBase("foo-folder", "Library", false); bio = " Already a successful comedian, Ben Elton turned to writing situation comedies during the 1980s and penned BBC classics such as \"The Young Ones\", \"Blackadder\", and during the 1990s \"The Thin Blue Line\".\n" + " He provided lyrics for Andrew Lloyd Webber Musical, The Beautiful Game, which was nominated for Best Musical at the Laurence Olivier Theatre Awards in 2001 (2000 season).\n" + " His comedy, Popcorn performed at the Apollo Theatre, was awarded the 1998 Laurence Olivier Theatre Award for Best New Comedy of the 1997 season.\n" + " Has three children : Bert, Lottie and Fred. Is co-writer of the Queen Musical 'We Will Rock You' with the band itself.\n" + "Birth name: Benjamin Charles Elton. Height: 5' 8\" (1.73 m) \n"; TreeNodeBase authorNode = new ExtendedTreeNode("author", "Ben Elton", false, bio); TreeNodeBase bookNode = new TreeNodeBase("book", "Post Mortem", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Blast from the Past", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Stark", false); authorNode.getChildren().add(bookNode); treeData.getChildren().add(authorNode); bio = "Nobody had ever won the Hugo and Nebula awards for best novel two years in a row, until Orson Scott Card received them for Ender's Game and its sequel, Speaker for the Dead, in 1986 and 1987. The third novel in the series, Xenocide, was published in 1991, and the fourth and seemingly final volume, Children of the Mind, was published in August 1996. However, the Ender cycle now includes the new parallel series that began with Ender's Shadow in 1999, followed by Shadow of the Hegemon in 2001, and continued with Shadow Puppets in 2002. Warner Brothers also recently announced that it has made a deal for director Wolfgang Petersen to bring Ender's Game to the big screen. Born in Richland, Washington, Card grew up in California, Arizona, and Utah. He lived in Brazil for two years as an unpaid missionary for the Mormon Church. He received degrees from Brigham Young University (1975) and the University of Utah (1981). He currently lives in Greensboro, North Carolina. He and his wife, Kristine, are the parents of five children: Geoffrey, Emily, Charles, Zina Margaret, and Erin Louisa (named for Chaucer, Bronte and Dickinson, Dickens, Mitchell, and Alcott, respectively)."; authorNode = new ExtendedTreeNode("author", "Orson Scott Card", false, bio); bookNode = new TreeNodeBase("book", "Ender's Game", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Xenocide", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Shadow Puppets", false); authorNode.getChildren().add(bookNode); treeData.getChildren().add(authorNode); bio = "Rod Johnson is an enterprise Java architect with extensive experience in the insurance, dot-com, and financial industries. He was the J2EE architect of one of Europe's largest web portals, and he has worked as a consultant on\n" + "a wide range of projects.\n Rod has an arts degree majoring in music and computer science from the University of Sydney. He obtained a Ph.D. in musicology before returning to software development. He has been working with both Java and J2EE since\n" + "their release and is actively involved in the Java Community Process as a member of the JSR-154 (Servlet 2.4) and JDO 2.0 Expert Groups. He is the author of several best-selling books, like \"Expert One-on-One J2EE Design and Development\" (Wrox, 2002) and has contributed to several other books on J2EE since 2000. As founder of the Spring Framework (www.springframework.org), he speaks frequently at leading industry conferences.\n"; authorNode = new ExtendedTreeNode("author", "Rod Johnson", false, bio); bookNode = new TreeNodeBase("book", "J2EE Design and Development", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "J2EE without Enterprise Java Beans", false); authorNode.getChildren().add(bookNode); bookNode = new TreeNodeBase("book", "Professional Java Development With The Spring Framework", false); authorNode.getChildren().add(bookNode); treeData.getChildren().add(authorNode); return treeData; } }
This
is all it takes to create a dynamic tree, driven from a backend data
model, including the select node action that updates the current
biography. In a next article I will actually use the EJB 3.0
Persistence functionality out of container (the GlassFish
implementation) for providing the backend data model. I will try to
make the nodes updateable as well.
Resources
The JDeveloper 10.1.3 MyFacesBase Application Workspace and Project: MyFacesBase.zip
The JDeveloper 10.1.3 Application Workspace as it is at the end of this article: MyFacesTree.zip
Overview of the Apache MyFaces Components – more specifically the Tree2 component
Despite the time this article is very useful, i have to support to a legacy jsf application and this article was very useful.
Thanks, thanks and again thanks!.
Hi, Also, I want to develop such a tree with checkboxes.
Any idea, please?
Hi Lucas,
I have gone through all your tree examples and they have been extremely helpful to me on my projects and application development. I am having one issue, which is setting focus on node (by default) when the tree rendered first time. I tried everything but could not achieve it.
My use case is when tree is rendered first time then one of the first level node (say Ben Elton), should be expanded and one of the child node (say Post Mortem), should be highlighted/ focused.
How can we achieve it?
Thanks
Best of the best example in a field where we rarely find a good example. This is really a big help for those starting with tree2. Thank you very much for posting.
I got it working in Exadel studio as well.
Thanks,
Sanjeev.
this is very good example for those who dont knoe abt tree structure.. and its really helps alot .as i was a beginer so i know how its helps me. thank you monu
This is an Excellent article but what about when i am not aware of hierarchy depth of tree in advance? Here it seems as if we must be awre of depth of tree i.e. from root node to lower most leaf. depending on that we design facet child and develop the application. please let me know the solution if we can use tree2 for a situation where i am not aware of depth of tree.
Thank you
Arvind
Hi,the code is really excellant.Author used jdeveloper,but iam using Exadel studio…So initially i was unable to import the MyFacesBase.jws in my eadel studio….But i used the code in this page to execute the tree.The LibraryHandler class should be renamed as LibraryTreeHandler.class,then only it works..
If any need to execute the app. in exadel..contact me..i will help u.
deepak.kannadasan@gmail.com
Excelent. O very good article.
I’ve started to understand what is about this tree2 from myfaces.
Thanks a lot for this.
This is the best step-by-step article.
Hi, I want to develop such a tree with checkboxes and multiple selections.
Any idea ???
This is An excellent article for tree2.
This examples works properly without tiles. But when
we use this with tiles it does not work i.e. does not
get expanded when we click on + symbol of the tree.
But tree component works well with tiles. Iwant to use
tree2 with tiles because my application is already developped
with tree2.
In my application tree2 component has to be displayed
on left side of the screen and depending on the click of node
coresponding information has to displayed on right hand side.
Please help me.
Thanks for this i was looking for a simple getting started example. I got this working (nearly fully) on eclipse.
Vijay
How could I adapt this such that when one of the links is clicked, a jsf page is displayed in the RH pane while the tree is still displayed.
Thanks
Angus
An excellent article that Oracle and Apache should have at the top of their documentation list for JDeveloper and MyFaces.
Thank you Lucas for giving what every developer wants when starting out with a new API – a straightforward A-Z explanation of how to get up and running.
Angus
Excellent article. It’s just a shame that you don’t explain how to use the tree2 component outside of JDeveloper…
Thanks.