A tree is a hierarchical component that shows the visual representation of the parent-child relationship between nodes.

Three Main Variations

Basic Tree

A basic tree provides a tree structure with named nodes and an arrow to expand and collapse child nodes.

Basic Tree With Icons

A tree can include icons to represent the type of nodes within that group. Icons appear between the collapse/expand arrow and the parent node title.

When using icons in a tree, make sure to use them on all nodes. Do not alternate between using and not using icons within the same tree.

If using a tree in the context of other components, make sure those icons are distinctive and easily recognizable. Use neutral colors to avoid overloading the tree and related components with colors and shapes.

Checkbox Tree

A checkbox tree features checkboxes between the collapse/expand arrow and the name to indicate whether a node is selected. A parent node with children that are both selected and not selected is shown with an “indeterminate” state.

Checkbox trees should not be used together with icons for the nodes. As with icon trees, make sure to put checkboxes on all nodes of a checkbox tree. Do not alternate between types of trees in a checkbox tree.

Style

The styling of each piece of a tree node is consistent across the different types of trees.

Touch Targets

The dimensions of the expand/collapse arrow and the node title allow for a comfortable touch target allowing use with a mouse or a touch screen.

Touch Targets
Touch Targets are relevant even outside of a mobile form factor. Many new desktop environments, especially those running Windows, allow for the use of a touch screen and should be considered when designing your applications.

Behavior

To explain the behavior of the tree, here is the terminology we’ll be using in this section:

Highlight

Click on a node in the tree to either “highlight” it or navigate to its relative content.

Select

Choose items to apply an action. For example, selecting a checkbox in the tree.

Expand / Collapse

Use the arrow to the left of a node to expand or collapse a node in the tree.

Interacting With Nodes

Expanding / Collapsing Nodes

To expand or collapse a parent node, the user clicks on the expand/collapse arrow. Clicking on the node item itself does not expand or collapse a node. It serves as a highlighting mechanism.

In read-only trees where highlighting is not an option, this pattern remains true for consistency.

Interacting With Nodes

Highlighting Tree Nodes

To navigate to a content area based on a tree node or to highlight a tree node in order to take a subsequent action based on the selection, a user clicks on the node title itself.

Highlighting Tree Nodes

Interacting With Checkbox Trees

With a checkbox tree, a user is able to perform one or a combination of three actions by clicking on one of three distinct targets:

  1. Expanding and Collapsing: a user is able to perform this action by clicking on the expand / collapse arrow.
  2. Checking a Checkbox: this would require clicking on the checkbox itself to check or uncheck a treenode. This will also affect the status of the parent node’s checkbox.
  3. Highlighting Tree Node: a user can highlight a tree node by clicking on the name (label) of the tree node. This allows for the possibility of loading content based on selection to provide more information on a tree node.
Interacting with Checkbox Trees

Loading Data

The way to load data within the tree is based on the scenario in which the tree is being used.

Load Parent Nodes First

With a dynamic tree, make sure to load the parent nodes first and then lazy load child nodes when requested.

A general goal to keep in mind is that you want to minimize the time a user needs to spend before their first interaction with the tree as well as every subsequent interaction afterwards.

Loading Data

Code & Examples

Basic Tree

A basic tree can be created by simply nesting clr-tree-node components at will. To pre-expand a node, you can use the [clrExpanded] input.

Back to navigation

Keeping track of expanded nodes

If you need to keep track of whether certain nodes are expanded or collapsed, each tree node offers a [(clrExpanded)] boolean two-way binding.

Back to navigation

Routing using a Tree

Use the .clr-treenode-link class to style content inside of a Tree Node as clickable. To indicate an active Tree Node use the .active class along with .clr-treenode-link.

Back to navigation

Generating A Tree Dynamically

Back to navigation

Checkbox Tree

Selection on the whole tree is enabled as soon as one of the nodes uses the [(clrSelected)] two-way binding. This two-way binding can receive either booleans or ClrSelectedState enum values, which can be UNSELECTED, SELECTED or INDETERMINATE. It will however always output ClrSelectedState enum values because the node could become indeterminate, so true two-way binding should be used on a ClrSelectedState property.

As with any two-way binding, the input can be used to pre-select nodes or dynamically select them due to external actions.

Binding selection to a boolean

If you know a specific node can never become indeterminate, you probably want to use a boolean property on your node. As mentioned previously, [(clrSelected)] always outputs ClrSelectedState enum values, making two-way binding with a boolean problematic. The most straightforward solution is to use the de-sugarized syntax of the two-way binding , transforming the output to a boolean directly.

Back to navigation

Recursive tree

If the data you are displaying is recursive or has an unknown depth, you can use our *clrRecursiveFor structural directive to recursively iterate over your data. It has the same syntax as *ngFor, and accepts an additional getChildren parameter that receives a node and should return its children. Please note that it needs to be used inside of a <clr-tree> to function properly.

Back to navigation

Lazy-loading children

If your tree is too large to be fully build on initialization or getting the children of a node is an expensive operation like an HTTP request, you might want to lazy-load tree nodes, only loading the ones that are currently displayed. To lazy-load children for a simple tree component, you need to combine several features as follows:

  • use our <clr-tree> root component, giving it a [clrLazy]="true" input,
  • leverage our *clrIfExpanded structural directive to only instantiate children when they are displayed,
  • listen to the (clrIfExpandedChange) output to fetch the children's data,
  • add a [clrLoading] boolean input to the node if fetching children is asynchronous, to display a spinner while waiting for the data to be loaded.

Back to navigation

Lazy-loading and selection

When lazy-loading nodes in a tree, Clarity only has partial information on the overall data and cannot link parent and children state as it does in the previous cases. For instance, in a lazy-loaded file system tree, our tree component does not know the contents of a folder until it is expanded. This means that if the user selects the folder without expanding it first we cannot output the selection change for the files, only for the folder itself. It is up to the application or up to the server to preserve consistency, ensuring files in the folder are selected if the folder itself is.

Back to navigation

Lazy-loading recursive trees

Lazy-loading data for recursive trees is actually the simplest case: as soon as you set [clrLazy]="true" on the parent <clr-tree>, the getChildren function will only be lazily called when a node becomes expanded, and supports both Promise and Observable return types if you need to fetch children asynchronously.

By default, recursive trees will pre-load on level ahead to know if the currently displayed nodes are expandable of not. If you do not want this behavior and have a way of knowing if a node is expandable without fetching its children, you can prevent the extra loading by using the [clrExpandable] boolean input on nodes based on your own condition.

Back to navigation

Summary of Options

Tree <clr-tree>

Input/Output Values Default Effect
[(clrLazy)]
Type: boolean
Default: false
boolean false Indicates to the tree that children nodes should be lazy-loaded

Tree Node <clr-tree-node>

Input/Output Values Default Effect
[(clrSelected)]
Type: ClrSelectedState
Default: UNSELECTED
ClrSelectedState UNSELECTED Two-way binding on the state of a Tree Node: UNSELECTED, SELECTED or INDETERMINATE.
[(clrExpanded)]
Type: boolean
Default: false
boolean false Two-way binding on the expanded/collapsed state of the node.
[clrExpandable]
Type: boolean | undefined
Default: undefined
boolean | undefined undefined Forces a node to be expandable or node, regardless of its children.

Recursive iterator *clrRecursiveFor

Input/Output Values Default Effect
[clrRecursiveForOf]
Type: T | T[]
Default: none
T | T[] none Root node(s) of the recursive tree, to begin the iteration.
[clrRecursiveForGetChildren]
Type: T => T[] | Promise<T[]> | Observable<T[]>
Default: none
T => T[] | Promise<T[]> | Observable<T[]> none The function to call in order to iterate over the children of a node. Can return an asynchronous result in the form of a Promise or an Observable.

*clrIfExpanded directive

Input/Output Values Default Effect
[(clrIfExpanded)]
Type: boolean
Default: false
boolean false Two-way binding on the expanded/collapsed state of the node.

Loading directive [clrLoading]

Input/Output Values Default Effect
[clrLoading]
Type: boolean
Default: false
boolean false Activates a spinner to indicate that children nodes are loading.