• cdi
  • components
  • contexts
  • converters
  • eventlisteners
  • exceptionhandlers
  • facesviews
  • filters
  • functions
  • managedbeans
  • renderkits
  • resourcehandlers
  • scripts
  • taghandlers
  • utils
  • validators
  • viewhandlers
-
  • cache
  • commandScript
  • componentIdParam
  • conditionalComment
  • deferredScript
  • form
  • graphicImage
  • highlight
  • messages
  • moveComponent
  • onloadScript
  • outputFormat
  • outputLabel
  • param
  • resolveComponent
  • resourceInclude
  • tree
  • viewParam

The <o:tree> allows the developers to have full control over the markup of a tree hierarchy by declaring the appropriate JSF components or HTML elements in the markup. The <o:tree> does namely not render any HTML markup by itself.

The component value must point to a tree of data objects represented by a TreeModel instance, typically established via a ValueExpression. During iterative processing over the nodes of tree in the tree model, the object for the current node is exposed as a request attribute under the key specified by the var attribute. The node itself is exposed as a request attribute under the key specified by the varNode attribute.

Only children of type TreeNode are allowed and processed by this component.

Here is a basic usage example:

 <o:tree value="#{bean.treeModel}" var="item" varNode="node">
     <o:treeNode>
         <ul>
             <o:treeNodeItem>
                 <li>
                     #{node.index} #{item.someProperty}
                     <o:treeInsertChildren />
                 </li>
             </o:treeNodeItem>
         </ul>
     </o:treeNode>
 </o:tree>

Showcase

Note that the left menu of this showcase application is also using an <o:tree> which is dynamically populated in an application scoped bean based on the structure of the webapp's /showcase folder. See also App source code, Page source code and layout.xhtml source code. Also note that Page extends ListTreeModel, this is not necessary per se, but it eases accessing the parent and sister pages in EL as is done in the navigation menu here above, and the siblings as is done in the quick navigation buttons right above. See also showcase.xhtml source code.

If you need a tree whereby the children are sorted based on their Comparable implementation, then use SortedTreeModel instead of ListTreeModel.

Demo

In the below editable tree example, all input values are required. Clear some of them and then submit to see proper validation message handling. If all values are valid, then the static tree will also be updated with submitted values. You can also dynamically add/remove nodes.

Static tree

0 One

  • 0_0 Two
  • 0_1 Three

1 Four

  • 1_0 Five

Editable tree

(only for this view scope ;) )
Source code
<p>
    In the below editable tree example, all input values are required. Clear some of them and then submit to see
    proper validation message handling. If all values are valid, then the static tree will also be updated with
    submitted values. You can also dynamically add/remove nodes.
</p>

<h3>Static tree</h3>
<h:panelGroup id="staticTree">
    <o:tree value="#{treeBean.tree}" var="exampleEntity" varNode="node">
        <o:treeNode level="0">
            <o:treeNodeItem>
                <h4>#{node.index} #{exampleEntity.value}</h4>
                <o:treeInsertChildren />
            </o:treeNodeItem>
        </o:treeNode>
        <o:treeNode>
            <ul>
                <o:treeNodeItem>
                    <li>
                        #{node.index} #{exampleEntity.value}
                        <o:treeInsertChildren />
                    </li>
                </o:treeNodeItem>
            </ul>
        </o:treeNode>
    </o:tree>
</h:panelGroup>

<hr/>

<h3>Editable tree</h3>
<h:form id="form">
    <c:set var="saveButtonPressed" value="#{param['javax.faces.source'] == 'form:save'}" />
    <h:commandButton id="addNode" value="Add new node" action="#{treeBean.addChild(treeBean.tree)}">
        <f:ajax execute="@form" render="@form" />
    </h:commandButton>
    <o:tree id="tree" value="#{treeBean.tree}" var="exampleEntity" varNode="node">
        <o:treeNode>
            <ul>
                <o:treeNodeItem>
                    <li>
                        <h:inputText id="value" value="#{exampleEntity.value}" styleClass="treeinput"
                            required="#{saveButtonPressed}" requiredMessage="Please enter value" />
                        <h:commandButton id="addChild" value="Add new child" action="#{treeBean.addChild(node)}"
                            rendered="#{node.level lt 10}">
                            <f:ajax execute="@form" render="@form" />
                        </h:commandButton>
                        <h:commandButton id="remove" value="Remove node" action="#{treeBean.remove(node)}">
                            <f:ajax execute="@form" render="@form" />
                        </h:commandButton>
                        <h:message for="value" />
                        <o:treeInsertChildren />
                    </li>
                </o:treeNodeItem>
            </ul>
        </o:treeNode>
    </o:tree>
    <h:commandButton id="save" value="Save" action="#{treeBean.save}">
        <f:ajax execute="@form" render="@form" />
    </h:commandButton> (only for this view scope ;) )
    <h:outputText value="OK!" rendered="#{facesContext.postback and not facesContext.validationFailed}" />
</h:form>

<h:outputScript>
    /**
     * Force input elements to invoke "Save" button on enter key instead of one of those "add"/"remove" buttons.
     */
    $(document).on("keypress", ".treeinput", function(event) {
        if (event.keyCode == 13) {
            $(this).closest("form").find("[id$=save]").click();
            return false;
        }
    });
</h:outputScript>
<o:onloadScript>
    /**
     * Focus the first empty input element on every ajax response.
     */
    $(".treeinput:not([value]):first").focus();
</o:onloadScript>