- 

Available since OmniFaces 1.0

The <o:tree> allows the developers to have full control over the markup of a tree hierarchy by declaring the appropriate Faces 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.

The <o:tree> tag supports only child tags of type <o:treeNode>, representing parent tree nodes. There can be multiple <o:treeNode> tags, each representing a separate parent tree node level, so that different markup could be declared for each tree node level, if necessary. The <o:treeNode> tag in turn supports child tag <o:treeNodeItem> which represents each child of the current parent tree node. The <o:treeNodeItem> in turn supports child tag <o:treeInsertChildren> which represents the insertion point of the grand children.

Here is a basic usage example where each parent tree node level is treated the same way via a single <o:treeNode>:

<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>

treeNode

The <o:treeNode> represents the parent tree node. Within this component, the var attribute of the <o:tree> will expose the parent tree node. Each of its children is processed by <o:treeNodeItem> on which the var attribute of the <o:tree> in turn exposes each child of the parent tree node.

The optional level attribute can be used to specify for which tree node level as obtained by TreeModel.getLevel() the <o:treeNode> should be rendered. The root tree node has level 0. If the level attribute is unspecified, then the <o:treeNode> will be rendered for any tree node level which hasn't already a <o:treeNode level="x"> specified.

treeNodeItem

The <o:treeNodeItem> represents the child item of the parent tree note as represented by <o:treeNode>. Within this component, the var attribute of the parent <o:tree> component will expose the child tree node.

Within <o:treeNodeItem> you can use <o:treeInsertChildren> to declare the place where to recursively render the <o:treeNode> whereby the current child item is in turn interpreted as a parent tree node (i.e. where you'd like to insert the grand-children).

treeInsertChildren

The <o:treeInsertChildren> represents the insertion point for the grand children. This is in turn further interpreted as <o:treeNode>.

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 ;) )
Demo 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['jakarta.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>