Skip to content

Commit 5e5066a

Browse files
tinayuangaommalerba
authored andcommitted
docs(tree): add cdk tree examples to material-examples (#10426)
1 parent 997cae5 commit 5e5066a

13 files changed

+358
-17
lines changed

src/cdk/tree/tree.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ these two types of trees.
77

88
#### Flat tree
99

10+
<!-- example(cdk-tree-flat) -->
11+
12+
1013
In a flat tree, the hierarchy is flattened; nodes are not rendered inside of each other, but instead
1114
are rendered as siblings in sequence. An instance of `TreeFlattener` is used to generate the flat
1215
list of items from hierarchical data. The "level" of each tree node is read through the `getLevel`
@@ -28,6 +31,8 @@ variations, such as infinite or virtual scrolling.
2831

2932
#### Nested tree
3033

34+
<!-- example(cdk-tree-nested) -->
35+
3136
In nested tree, children nodes are placed inside their parent node in DOM. The parent node contains
3237
a node outlet into which children are projected.
3338

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.demo-tree-node {
2+
display: flex;
3+
align-items: center;
4+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
2+
<cdk-tree-node *cdkTreeNodeDef="let node" cdkTreeNodePadding class="demo-tree-node">
3+
<button mat-icon-button disabled></button>
4+
{{node.filename}}: {{node.type}}
5+
</cdk-tree-node>
6+
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding class="demo-tree-node">
7+
<button mat-icon-button [attr.aria-label]="'toggle ' + node.filename" cdkTreeNodeToggle>
8+
<mat-icon>{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}</mat-icon>
9+
</button>
10+
{{node.filename}}: {{node.type}}
11+
</cdk-tree-node>
12+
</cdk-tree>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import {Component, Injectable} from '@angular/core';
2+
import {FlatTreeControl} from '@angular/cdk/tree';
3+
import {MatTreeFlattener, MatTreeFlatDataSource} from '@angular/material/tree';
4+
import {of} from 'rxjs/observable/of';
5+
import {Observable} from 'rxjs/Observable';
6+
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
7+
8+
/**
9+
* File node data with nested structure.
10+
* Each node has a filename, and a type or a list of children.
11+
*/
12+
export class FileNode {
13+
children: FileNode[];
14+
filename: string;
15+
type: any;
16+
}
17+
18+
/** Flat node with expandable and level information */
19+
export class FileFlatNode {
20+
filename: string;
21+
type: any;
22+
level: number;
23+
expandable: boolean;
24+
}
25+
26+
/**
27+
* The file structure tree data in string. The data could be parsed into a Json object
28+
*/
29+
const TREE_DATA = `
30+
{
31+
"Documents": {
32+
"angular": {
33+
"src": {
34+
"core": "ts",
35+
"compiler": "ts"
36+
}
37+
},
38+
"material2": {
39+
"src": {
40+
"button": "ts",
41+
"checkbox": "ts",
42+
"input": "ts"
43+
}
44+
}
45+
},
46+
"Downloads": {
47+
"Tutorial": "html",
48+
"November": "pdf",
49+
"October": "pdf"
50+
},
51+
"Pictures": {
52+
"Sun": "png",
53+
"Woods": "jpg",
54+
"Photo Booth Library": {
55+
"Contents": "dir",
56+
"Pictures": "dir"
57+
}
58+
},
59+
"Applications": {
60+
"Chrome": "app",
61+
"Calendar": "app",
62+
"Webstorm": "app"
63+
}
64+
}`;
65+
66+
/**
67+
* File database, it can build a tree structured Json object from string.
68+
* Each node in Json object represents a file or a directory. For a file, it has filename and type.
69+
* For a directory, it has filename and children (a list of files or directories).
70+
* The input will be a json object string, and the output is a list of `FileNode` with nested
71+
* structure.
72+
*/
73+
@Injectable()
74+
export class FileDatabase {
75+
dataChange: BehaviorSubject<FileNode[]> = new BehaviorSubject<FileNode[]>([]);
76+
77+
get data(): FileNode[] { return this.dataChange.value; }
78+
79+
constructor() {
80+
this.initialize();
81+
}
82+
83+
initialize() {
84+
// Parse the string to json object.
85+
const dataObject = JSON.parse(TREE_DATA);
86+
87+
// Build the tree nodes from Json object. The result is a list of `FileNode` with nested
88+
// file node as children.
89+
const data = this.buildFileTree(dataObject, 0);
90+
91+
// Notify the change.
92+
this.dataChange.next(data);
93+
}
94+
95+
/**
96+
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
97+
* The return value is the list of `FileNode`.
98+
*/
99+
buildFileTree(value: any, level: number): FileNode[] {
100+
let data: any[] = [];
101+
for (let k in value) {
102+
let v = value[k];
103+
let node = new FileNode();
104+
node.filename = `${k}`;
105+
if (v === null || v === undefined) {
106+
// no action
107+
} else if (typeof v === 'object') {
108+
node.children = this.buildFileTree(v, level + 1);
109+
} else {
110+
node.type = v;
111+
}
112+
data.push(node);
113+
}
114+
return data;
115+
}
116+
}
117+
118+
/**
119+
* @title Tree with flat nodes
120+
*/
121+
@Component({
122+
selector: 'cdk-tree-flat-example',
123+
templateUrl: 'cdk-tree-flat-example.html',
124+
styleUrls: ['cdk-tree-flat-example.css'],
125+
providers: [FileDatabase]
126+
})
127+
export class CdkTreeFlatExample {
128+
129+
treeControl: FlatTreeControl<FileFlatNode>;
130+
131+
treeFlattener: MatTreeFlattener<FileNode, FileFlatNode>;
132+
133+
dataSource: MatTreeFlatDataSource<FileNode, FileFlatNode>;
134+
135+
constructor(database: FileDatabase) {
136+
this.treeFlattener = new MatTreeFlattener(this.transformer, this._getLevel,
137+
this._isExpandable, this._getChildren);
138+
this.treeControl = new FlatTreeControl<FileFlatNode>(this._getLevel, this._isExpandable);
139+
this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
140+
141+
database.dataChange.subscribe(data => {
142+
this.dataSource.data = data;
143+
});
144+
}
145+
146+
transformer = (node: FileNode, level: number) => {
147+
let flatNode = new FileFlatNode();
148+
flatNode.filename = node.filename;
149+
flatNode.type = node.type;
150+
flatNode.level = level;
151+
flatNode.expandable = !!node.children;
152+
return flatNode;
153+
}
154+
155+
private _getLevel = (node: FileFlatNode) => { return node.level; };
156+
157+
private _isExpandable = (node: FileFlatNode) => { return node.expandable; };
158+
159+
private _getChildren = (node: FileNode): Observable<FileNode[]> => { return of(node.children); };
160+
161+
hasChild = (_: number, _nodeData: FileFlatNode) => { return _nodeData.expandable; };
162+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.example-tree-invisible {
2+
display: none;
3+
}
4+
5+
.example-tree ul,
6+
.example-tree li {
7+
margin-top: 0;
8+
margin-bottom: 0;
9+
list-style-type: none;
10+
}
11+
12+
.example-tree-node {
13+
display: block;
14+
padding-left: 40px;
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<cdk-tree [dataSource]="nestedDataSource" [treeControl]="nestedTreeControl">
2+
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="example-tree-node">
3+
<button mat-icon-button disabled></button>
4+
{{node.filename}}: {{node.type}}
5+
</cdk-nested-tree-node>
6+
<cdk-nested-tree-node *cdkTreeNodeDef="let node; when: hasNestedChild" class="example-tree-node">
7+
<button mat-icon-button [attr.aria-label]="'toggle ' + node.filename" cdkTreeNodeToggle>
8+
<mat-icon>{{nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}</mat-icon>
9+
</button>
10+
{{node.filename}}: {{node.type}}
11+
<div [class.example-tree-invisible]="!nestedTreeControl.isExpanded(node)">
12+
<ng-container cdkTreeNodeOutlet></ng-container>
13+
</div>
14+
</cdk-nested-tree-node>
15+
</cdk-tree>
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import {Component, Injectable} from '@angular/core';
2+
import {NestedTreeControl} from '@angular/cdk/tree';
3+
import {MatTreeNestedDataSource} from '@angular/material/tree';
4+
import {of} from 'rxjs/observable/of';
5+
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
6+
7+
/**
8+
* Json node data with nested structure. Each node has a filename and a value or a list of children
9+
*/
10+
export class FileNode {
11+
children: FileNode[];
12+
filename: string;
13+
type: any;
14+
}
15+
16+
/**
17+
* The Json tree data in string. The data could be parsed into Json object
18+
*/
19+
const TREE_DATA = `
20+
{
21+
"Documents": {
22+
"angular": {
23+
"src": {
24+
"core": "ts",
25+
"compiler": "ts"
26+
}
27+
},
28+
"material2": {
29+
"src": {
30+
"button": "ts",
31+
"checkbox": "ts",
32+
"input": "ts"
33+
}
34+
}
35+
},
36+
"Downloads": {
37+
"Tutorial": "html",
38+
"November": "pdf",
39+
"October": "pdf"
40+
},
41+
"Pictures": {
42+
"Sun": "png",
43+
"Woods": "jpg",
44+
"Photo Booth Library": {
45+
"Contents": "dir",
46+
"Pictures": "dir"
47+
}
48+
},
49+
"Applications": {
50+
"Chrome": "app",
51+
"Calendar": "app",
52+
"Webstorm": "app"
53+
}
54+
}`;
55+
56+
/**
57+
* File database, it can build a tree structured Json object from string.
58+
* Each node in Json object represents a file or a directory. For a file, it has filename and type.
59+
* For a directory, it has filename and children (a list of files or directories).
60+
* The input will be a json object string, and the output is a list of `FileNode` with nested
61+
* structure.
62+
*/
63+
@Injectable()
64+
export class FileDatabase {
65+
dataChange: BehaviorSubject<FileNode[]> = new BehaviorSubject<FileNode[]>([]);
66+
67+
get data(): FileNode[] { return this.dataChange.value; }
68+
69+
constructor() {
70+
this.initialize();
71+
}
72+
73+
initialize() {
74+
// Parse the string to json object.
75+
const dataObject = JSON.parse(TREE_DATA);
76+
77+
// Build the tree nodes from Json object. The result is a list of `FileNode` with nested
78+
// file node as children.
79+
const data = this.buildFileTree(dataObject, 0);
80+
81+
// Notify the change.
82+
this.dataChange.next(data);
83+
}
84+
85+
/**
86+
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
87+
* The return value is the list of `FileNode`.
88+
*/
89+
buildFileTree(value: any, level: number): FileNode[] {
90+
let data: any[] = [];
91+
for (let k in value) {
92+
let v = value[k];
93+
let node = new FileNode();
94+
node.filename = `${k}`;
95+
if (v === null || v === undefined) {
96+
// no action
97+
} else if (typeof v === 'object') {
98+
node.children = this.buildFileTree(v, level + 1);
99+
} else {
100+
node.type = v;
101+
}
102+
data.push(node);
103+
}
104+
return data;
105+
}
106+
}
107+
108+
/**
109+
* @title Tree with nested nodes
110+
*/
111+
@Component({
112+
selector: 'cdk-tree-nested-example',
113+
templateUrl: 'cdk-tree-nested-example.html',
114+
styleUrls: ['cdk-tree-nested-example.css'],
115+
providers: [FileDatabase]
116+
})
117+
export class CdkTreeNestedExample {
118+
nestedTreeControl: NestedTreeControl<FileNode>;
119+
120+
nestedDataSource: MatTreeNestedDataSource<FileNode>;
121+
122+
constructor(database: FileDatabase) {
123+
this.nestedTreeControl = new NestedTreeControl<FileNode>(this._getChildren);
124+
this.nestedDataSource = new MatTreeNestedDataSource();
125+
126+
database.dataChange.subscribe(data => this.nestedDataSource.data = data);
127+
}
128+
129+
private _getChildren = (node: FileNode) => { return of(node.children); };
130+
131+
hasNestedChild = (_: number, nodeData: FileNode) => {return !(nodeData.type); };
132+
}
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +0,0 @@
1-
.example-tree ul,
2-
.example-tree li {
3-
-webkit-margin-before: 0;
4-
-webkit-margin-after: 0;
5-
list-style-type: none;
6-
}

src/material-examples/tree-flat-overview/tree-flat-overview-example.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl" class="example-tree">
1+
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
22
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding>
3+
<button mat-icon-button disabled></button>
34
{{node.filename}} : {{node.type}}
45
</mat-tree-node>
56

0 commit comments

Comments
 (0)