220 lines
7.8 KiB
Markdown
220 lines
7.8 KiB
Markdown
|
|
The `mat-tree` provides a Material Design styled tree that can be used to display hierarchical
|
|||
|
|
data.
|
|||
|
|
|
|||
|
|
This tree builds on the foundation of the CDK tree and uses a similar interface for its
|
|||
|
|
data source input and template, except that its element and attribute selectors will be prefixed
|
|||
|
|
with `mat-` instead of `cdk-`.
|
|||
|
|
|
|||
|
|
There are two types of trees: flat and nested. The DOM structures are different for these
|
|||
|
|
two types of trees.
|
|||
|
|
|
|||
|
|
#### Flat tree
|
|||
|
|
|
|||
|
|
In a flat tree, the hierarchy is flattened; nodes are not rendered inside of each other,
|
|||
|
|
but instead are rendered as siblings in sequence.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree>
|
|||
|
|
<mat-tree-node> parent node </mat-tree-node>
|
|||
|
|
<mat-tree-node> -- child node1 </mat-tree-node>
|
|||
|
|
<mat-tree-node> -- child node2 </mat-tree-node>
|
|||
|
|
</mat-tree>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<!-- example(tree-flat-child-accessor-overview) -->
|
|||
|
|
|
|||
|
|
Flat trees are generally easier to style and inspect. They are also more friendly to scrolling
|
|||
|
|
variations, such as infinite or virtual scrolling.
|
|||
|
|
|
|||
|
|
#### Nested tree
|
|||
|
|
|
|||
|
|
In a nested tree, children nodes are placed inside their parent node in DOM. The parent node
|
|||
|
|
contains a node outlet into which children are projected.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree>
|
|||
|
|
<mat-nested-tree-node>
|
|||
|
|
parent node
|
|||
|
|
<mat-nested-tree-node> -- child node1 </mat-nested-tree-node>
|
|||
|
|
<mat-nested-tree-node> -- child node2 </mat-nested-tree-node>
|
|||
|
|
</mat-nested-tree-node>
|
|||
|
|
</mat-tree>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<!-- example(tree-nested-child-accessor-overview) -->
|
|||
|
|
|
|||
|
|
Nested trees are easier to work with when hierarchical relationships are visually represented in
|
|||
|
|
ways that would be difficult to accomplish with flat nodes.
|
|||
|
|
|
|||
|
|
### Usage
|
|||
|
|
|
|||
|
|
#### Writing your tree template
|
|||
|
|
|
|||
|
|
In order to use the tree, you must define a tree node template. There are two types of tree nodes,
|
|||
|
|
`<mat-tree-node>` for flat tree and `<mat-nested-tree-node>` for nested tree. The tree node
|
|||
|
|
template defines the look of the tree node, expansion/collapsing control and the structure for
|
|||
|
|
nested children nodes.
|
|||
|
|
|
|||
|
|
A node definition is specified via any element with `matNodeDef`. This directive exports the node
|
|||
|
|
data to be used in any bindings in the node template.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree-node *matNodeDef="let node">
|
|||
|
|
{{node.key}}: {{node.value}}
|
|||
|
|
</mat-tree-node>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### Flat tree node template
|
|||
|
|
|
|||
|
|
Flat trees use the `level` of a node to both render and determine hierarchy of the nodes for screen
|
|||
|
|
readers. This may be provided either via `levelAccessor`, or will be calculated by `MatTree` if
|
|||
|
|
`childrenAccessor` is provided.
|
|||
|
|
|
|||
|
|
Spacing can be added either by applying the `matNodePadding` directive or by applying custom styles
|
|||
|
|
based on the `aria-level` attribute.
|
|||
|
|
|
|||
|
|
|
|||
|
|
##### Nested tree node template
|
|||
|
|
|
|||
|
|
When using nested tree nodes, the node template must contain a `matTreeNodeOutlet`, which marks
|
|||
|
|
where the children of the node will be rendered.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-nested-tree-node *matNodeDef="let node">
|
|||
|
|
{{node.value}}
|
|||
|
|
<ng-container matTreeNodeOutlet></ng-container>
|
|||
|
|
</mat-nested-tree-node>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Adding expand/collapse
|
|||
|
|
|
|||
|
|
The `matTreeNodeToggle` directive can be used to add expand/collapse functionality for tree nodes.
|
|||
|
|
The toggle calls the expand/collapse functions in the `matTree` and is able to expand/collapse
|
|||
|
|
a tree node recursively by setting `[matTreeNodeToggleRecursive]` to true.
|
|||
|
|
|
|||
|
|
`matTreeNodeToggle` should be attached to button elements, and will trigger upon click or keyboard
|
|||
|
|
activation. For icon buttons, ensure that `aria-label` is provided.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree-node *matNodeDef="let node">
|
|||
|
|
<button matTreeNodeToggle aria-label="toggle tree node" [matTreeNodeToggleRecursive]="true">
|
|||
|
|
<mat-icon>expand</mat-icon>
|
|||
|
|
</button>
|
|||
|
|
{{node.value}}
|
|||
|
|
</mat-tree-node>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Toggle
|
|||
|
|
|
|||
|
|
A `matTreeNodeToggle` can be added in the tree node template to expand/collapse the tree node. The
|
|||
|
|
toggle toggles the expand/collapse functions in `TreeControl` and is able to expand/collapse a
|
|||
|
|
tree node recursively by setting `[matTreeNodeToggleRecursive]` to `true`.
|
|||
|
|
|
|||
|
|
The toggle can be placed anywhere in the tree node, and is only toggled by `click` action.
|
|||
|
|
|
|||
|
|
|
|||
|
|
### Padding (Flat tree only)
|
|||
|
|
|
|||
|
|
The `matTreeNodePadding` can be placed in a flat tree's node template to display the `level`
|
|||
|
|
information of a flat tree node.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree-node *matNodeDef="let node" matNodePadding>
|
|||
|
|
{{node.value}}
|
|||
|
|
</mat-tree-node>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
This is unnecessary for a nested tree, since the hierarchical structure of the DOM allows for
|
|||
|
|
padding to be added via CSS.
|
|||
|
|
|
|||
|
|
|
|||
|
|
#### Conditional template
|
|||
|
|
|
|||
|
|
The tree may include multiple node templates, where a template is chosen
|
|||
|
|
for a particular data node via the `when` predicate of the template.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree-node *matNodeDef="let node" matTreeNodePadding>
|
|||
|
|
{{node.value}}
|
|||
|
|
</mat-tree-node>
|
|||
|
|
<mat-tree-node *matNodeDef="let node; when: isSpecial" matTreeNodePadding>
|
|||
|
|
[ A special node {{node.value}} ]
|
|||
|
|
</mat-tree-node>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Data Source
|
|||
|
|
|
|||
|
|
#### Connecting the tree to a data source
|
|||
|
|
|
|||
|
|
Similar to `mat-table`, you can provide data to the tree through a `DataSource`. When the tree receives
|
|||
|
|
a `DataSource` it will call its `connect()` method which returns an observable that emits an array
|
|||
|
|
of data. Whenever the data source emits data to this stream, the tree will render an update.
|
|||
|
|
|
|||
|
|
Because the data source provides this stream, it bears the responsibility of toggling tree
|
|||
|
|
updates. This can be based on anything: tree node expansion change, websocket connections, user
|
|||
|
|
interaction, model updates, time-based intervals, etc.
|
|||
|
|
|
|||
|
|
There are two main methods of providing data to the tree:
|
|||
|
|
|
|||
|
|
* flattened data, combined with `levelAccessor`. This should be used if the data source already
|
|||
|
|
flattens the nested data structure into a single array.
|
|||
|
|
* only root data, combined with `childrenAccessor`. This should be used if the data source is
|
|||
|
|
already provided as a nested data structure.
|
|||
|
|
|
|||
|
|
#### `levelAccessor`
|
|||
|
|
|
|||
|
|
`levelAccessor` is a function that when provided a datum, returns the level the data sits at in the
|
|||
|
|
tree structure. If `levelAccessor` is provided, the data provided by `dataSource` should contain all
|
|||
|
|
renderable nodes in a single array.
|
|||
|
|
|
|||
|
|
The data source is responsible for handling node expand/collapse events and providing an updated
|
|||
|
|
array of renderable nodes, if applicable. This can be listened to via the `(expansionChange)` event
|
|||
|
|
on `mat-tree-node` and `mat-nested-tree-node`.
|
|||
|
|
|
|||
|
|
#### `childrenAccessor`
|
|||
|
|
|
|||
|
|
`childrenAccessor` is a function that when provided a datum, returns the children of that particular
|
|||
|
|
datum. If `childrenAccessor` is provided, the data provided by `dataSource` should _only_ contain
|
|||
|
|
the root nodes of the tree.
|
|||
|
|
|
|||
|
|
#### `trackBy`
|
|||
|
|
|
|||
|
|
To improve performance, a `trackBy` function can be provided to the tree similar to Angular’s
|
|||
|
|
[`ngFor` `trackBy`](https://angular.dev/api/common/NgForOf?tab=usage-notes). This informs the
|
|||
|
|
tree how to uniquely identify nodes to track how the data changes with each update.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl" [trackBy]="trackByFn">
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Accessibility
|
|||
|
|
|
|||
|
|
The `<mat-tree>` implements the [`tree` widget](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/),
|
|||
|
|
including keyboard navigation and appropriate roles and ARIA attributes.
|
|||
|
|
|
|||
|
|
In order to use the new accessibility features, migrating to `levelAccessor` and `childrenAccessor`
|
|||
|
|
is required. Trees using `treeControl` do not implement the correct accessibility features for
|
|||
|
|
backwards compatibility.
|
|||
|
|
|
|||
|
|
#### isExpandable
|
|||
|
|
|
|||
|
|
In order for the tree to correctly determine whether or not a node is expandable, the `isExpandable`
|
|||
|
|
property must be set on all `mat-tree-node` or `mat-tree-nested-node` that are expandable.
|
|||
|
|
|
|||
|
|
#### Activation actions
|
|||
|
|
|
|||
|
|
For trees with nodes that have actions upon activation or click, `<mat-tree-node>` will emit
|
|||
|
|
`(activation)` events that can be listened to when the user activates a node via keyboard
|
|||
|
|
interaction.
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<mat-tree-node
|
|||
|
|
*matNodeDef="let node"
|
|||
|
|
(click)="performAction(node)"
|
|||
|
|
(activation)="performAction($event)">
|
|||
|
|
</mat-tree-node>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
In this example, `$event` contains the node's data and is equivalent to the implicit data passed in
|
|||
|
|
the `matNodeDef` context.
|