Tree

You are viewing an old version (v. 14) of this page.
The latest version is v. 18, last edited on Oct 24, 2016 (view differences | )
<< View previous version | view page history | view next version >>

Overview

Since 3.2

The ace:tree component is used for the display of hierarchical data. The data can be supplied to the tree as a List of a javax.swing.tree.TreeNode implementer or as an instance of a NodeDataModel. For lazy loading cases an abstract implementation of NodeDataModel, LazyNodeDataModel should be implemented and supplied.

See the ICEfaces Showcase Live Demo of this component, complete with source code.

Getting Started

<ace:tree id="tree"
          expansion="true"
          selection="true"
          reordering="true"
          value="#{treeBean.treeRoots}"
          var="node"
          type="#{node.type}">
        <ace:node type="country">
            Country: <h:outputText value="#{node.name}" />
        </ace:node>
        <ace:node type="province">
            Province: <h:outputText value="#{node.name}" />
        </ace:node>
        // Default node type
        <ace:node>
            City: <h:outputText value="#{node.name}" />
        </ace:node>
</ace:tree>
    private List<LocationNodeImpl> treeRoots = TreeDataFactory.getTreeRoots();

    ...

    public List<LocationNodeImpl> getTreeRoots() {
        return treeRoots;
    }
public class TreeDataFactory {
    public static LocationNodeImpl getSingleRoot() {
        LocationNodeImpl[] bcCities = {
                new LocationNodeImpl("Vancouver",   "city",     1),
                new LocationNodeImpl("Kelowna",     "city",     1),
                new LocationNodeImpl("Kamloops",    "city",     1)
        };

        LocationNodeImpl[] abCities = {
                new LocationNodeImpl("Edmonton",    "city",     1),
                new LocationNodeImpl("Calgary",     "city",     1),
                new LocationNodeImpl("Red Deer",    "city",     1)
        };

        LocationNodeImpl[] onCities = {
                new LocationNodeImpl("Toronto",     "city",     1),
                new LocationNodeImpl("Waterloo",    "city",     1),
                new LocationNodeImpl("Ottawa",      "city",     1)
        };

        LocationNodeImpl[] mbCities = {
                new LocationNodeImpl("Winnipeg",    "city",     1),
                new LocationNodeImpl("Brandon",     "city",     1),
                new LocationNodeImpl("Churchill",   "city",     1)
        };

        LocationNodeImpl[] skCities = {
                new LocationNodeImpl("Regina",      "city",     1),
                new LocationNodeImpl("Saskatoon",   "city",     1),
                new LocationNodeImpl("Moose Jaw",   "city",     1)
        };

        LocationNodeImpl[] qbCities = {
                new LocationNodeImpl("Quebec City", "city",     1),
                new LocationNodeImpl("Montreal",    "city",     1),
                new LocationNodeImpl("Gatineau",    "city",     1)
        };

        LocationNodeImpl[] nbCities = {
                new LocationNodeImpl("Saint John",  "city",     1),
                new LocationNodeImpl("Moncton",     "city",     1),
                new LocationNodeImpl("Fredericton", "city",     1)
        };

        LocationNodeImpl[] nfCities = {
                new LocationNodeImpl("St. John's",      "city",     1),
                new LocationNodeImpl("Conception Bay",  "city",     1),
                new LocationNodeImpl("Mount Pearl",     "city",     1)
        };

        LocationNodeImpl[] nsCities = {
                new LocationNodeImpl("Halifax",     "city",     1),
                new LocationNodeImpl("Cape Breton", "city",     1),
                new LocationNodeImpl("Truro",       "city",     1)
        };

        LocationNodeImpl[] provinces = {
                new LocationNodeImpl("British Columbia",    "province",    1, bcCities),
                new LocationNodeImpl("Alberta",             "province",    1, abCities),
                new LocationNodeImpl("Saskatchewan",        "province",    1, skCities),
                new LocationNodeImpl("Manitoba",            "province",    1, mbCities),
                new LocationNodeImpl("Ontario",             "province",    1, onCities),
                new LocationNodeImpl("Quebec",              "province",    1, qbCities),
                new LocationNodeImpl("New Brunswick",       "province",    1, nbCities),
                new LocationNodeImpl("Newfoundland",        "province",    1, nfCities),
                new LocationNodeImpl("Nova Scotia",         "province",    1, nsCities),
        };

        return new LocationNodeImpl("Canada", "country", 1, provinces);
    }

    public static List<LocationNodeImpl> getTreeRoots() {
        return new ArrayList<LocationNodeImpl>() {{
            add(getSingleRoot());
        }};
    }
}
import org.apache.commons.collections.IteratorUtils;

import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.io.Serializable;
import java.util.*;

public class LocationNodeImpl implements MutableTreeNode, Serializable {
    LocationNodeImpl parent;
    List<LocationNodeImpl> children;
    String name;
    String type;
    Integer population;

    public LocationNodeImpl(String name, String type, Integer population, LocationNodeImpl[] children) {
        this.name = name;
        this.type = type;
        this.population = population;

        this.children = new ArrayList<LocationNodeImpl>(Arrays.asList(children));

        for (LocationNodeImpl t : children) {
            t.setupParent(this);
        }
    }

    public LocationNodeImpl(String name, String type, Integer population, Collection<LocationNodeImpl> children) {
        this.name = name;
        this.type = type;
        this.population = population;

        this.children = new ArrayList<LocationNodeImpl>(children);

        for (LocationNodeImpl t : children) {
            t.setupParent(this);
        }
    }

    public LocationNodeImpl(String name, String type, int population) {
        this.name = name;
        this.type = type;
        this.population = population;
    }

    public TreeNode getChildAt(int i) {
        if (children == null) return null;
        return children.get(i);
    }

    private boolean childrenSet() {
        return children != null && children.size() > 0;
    }

    public int getChildCount() {
        if (childrenSet())
            return children.size();
        else
            return 0;
    }

    public TreeNode getParent() {
        return parent;
    }

    public int getIndex(TreeNode treeNode) {
        return children.indexOf(treeNode);
    }

    public boolean getAllowsChildren() {
        return children != null;
    }

    public boolean isLeaf() {
        return getChildCount() == 0;
    }

    // Only to be used at inital construction as this
    // is not a mutable tree.
    public void setupParent(LocationNodeImpl parent) {
        this.parent = parent;
    }

    public Enumeration children() {
        if (children == null)
            return IteratorUtils.asEnumeration(IteratorUtils.emptyIterator());
        return IteratorUtils.asEnumeration(children.iterator());
    }

    public Integer getPopulation() {
        return population;
    }

    public void setPopulation(Integer population) {
        this.population = population;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void insert(MutableTreeNode mutableTreeNode, int i) {
        mutableTreeNode.setParent(this);
        children.add(i, (LocationNodeImpl)mutableTreeNode);
    }

    public void remove(int i) {
        children.remove(i);
    }

    public void remove(MutableTreeNode mutableTreeNode) {
        children.remove(mutableTreeNode);
    }

    public void setUserObject(Object o) {
        // Not required for any ace:tree functionality
        throw new UnsupportedOperationException();
    }

    public void removeFromParent() {
        if (parent != null)
            parent.remove(this);
    }

    public void setParent(MutableTreeNode mutableTreeNode) {
        parent = (LocationNodeImpl) mutableTreeNode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        LocationNodeImpl that = (LocationNodeImpl) o;

        if (!name.equals(that.name)) return false;
        if (!population.equals(that.population)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + population.hashCode();
        return result;
    }
}

For examples (and source code) of using Lazy Loading and Selection, please see the Live Demo.

Attributes

TagLib Documentation
This section covers attributes involved in the typical use-cases for this component. For reference, the complete taglib documentation for this component is available here.

expansion: Enable the expansion of nodes via clicks to the expansion icons.

selection: Enable the selection & deselection of nodes via clicks to the node body.

reordering: Enable the reordering of nodes by click-dragging the nodes.

value: The node data of the tree, a List of TreeNode objects or a NodeDataModel.

stateCreationCallback: Enables use of a NodeStateCreationCallback, which lazily initializes the component NodeStates based on a given node data object.

var: Specifies the request-scope attribute which will expose the current node data object.

stateMap: Define a NodeStateMap ValueExpression to access the store of ace:tree node data object state information. The state map provides an API for looking up the state of a particular node object, as well as reverse look-ups to get node objects with a particular state.

stateVar: Specifies the request-scope attribute which will expose the NodeState object the current node data object.

type: Defines a ValueExpression for a String that will evaluate per-node data object to determine which ace:node template will be rendered by matching that String against the type property of the ace:node components.

Client Behaviour Events

Name Description
select Fired when a node is clicked & selected.
deselect Fired when a node is clicked & deselected.
expand Fired when a node expansion switch is clicked to reveal subnodes.
contract Fired when a node expansion switch is clicked to hide subnodes.
reorder Fired when an item is dropped into a new position.

Client and Server Side Modes

The ace:tree component can work in both modes out of the box. The intuitive user interface in the client side manipulates the state of the tree as one may expect (i.e. expanding/contracting nodes). All the changes in the client side are reflected in the state of the tree in the server side (i.e. in the state map object org.icefaces.ace.model.tree.NodeStateMap). The converse is also true, all changes in the state of the tree made programmatically in the server side are reflected in the client side as well.

When setting the expansionMode and selectionMode attributes to client, every time the user contracts, expands, or selects a node, a request to the server WON'T be made. All the stat information will stay on the client until the containing form is submitted in another way,

CSS Classes

The following markup represents the basic HTML structure of the component and the CSS classes it uses.

<!-- Root container -->
<span class="if-tree if-node-sub ui-widget-content ui-corner-all ui-sortable">
	<table class="if-node-cnt">
		<tbody>
			<!-- 1st level node -->
			<tr class="if-node-tr">
				<td class="if-node-sw">
					<div class="if-node-ln">
						<img />
						<span class="ui-icon ui-icon-minus"></span>
					</div>
				</td>
				<td class="if-node">
					<div class="if-node-wrp">
						Item label
					</div>
				</td>
			</tr>
			<tr class="if-node-tr">
				<td class="if-node-td">
					<div class="if-node-ln">
						<img />
					</div>
				</td>
				<td class="if-node-sub ui-sortable">
					<table class="if-node-cnt">
						<tbody>
							<!-- 2nd level node -->
							<tr class="if-node-tr">
								<td class="if-node-sw">
									<div class="if-node-ln">
										<img />
										<span class="ui-icon ui-icon-minus"></span>
									</div>
								</td>
								<td class="if-node">
									<div class="if-node-wrp">
										Item label
									</div>
								</td>
							</tr>
							<tr class="if-node-tr">
								<td class="if-node-td">
									<div class="if-node-ln">
										<img />
									</div>
								</td>
								<td class="if-node-sub ui-sortable">
									<table class="if-node-cnt">
										<tbody>
											<!-- 3rd level node -->
											<tr class="if-node-tr">
												<td class="if-node-sw">
													<div class="if-node-ln">
														<img />
													</div>
												</td>
												<td class="if-node">
													<div class="if-node-wrp">
														Item label
													</div>
												</td>
											</tr>
											<tr class="if-node-tr">
												<td class="if-node-td">
													<div class="if-node-ln">
														<img />
													</div>
												</td>
												<td class="if-node-sub"></td>
											</tr>
										</tbody>
									</table>
								</td>
							</tr>
						</tbody>
					</table>
					<table class="if-node-cnt">
						<tbody>
							<!-- 1st level node -->
							<tr class="if-node-tr">
								<td class="if-node-sw">
									<div class="if-node-ln">
										<img />
										<span class="ui-icon ui-icon-plus"></span>
									</div>
								</td>
								<td class="if-node">
									<div class="if-node-wrp">
										Item label
									</div>
								</td>
							</tr>
							<tr class="if-node-tr">
								<td class="if-node-td">
									<div class="if-node-ln">
										<img />
									</div>
								</td>
								<td class="if-node-sub ui-sortable"></td>
							</tr>
						</tbody>
					</table>
				</td>
			</tr>
		</tbody>
	</table>
</span>
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

© Copyright 2017 ICEsoft Technologies Canada Corp.