Skip to content

docs(tree): add cdk tree examples to material-examples #10426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/cdk/tree/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ these two types of trees.

#### Flat tree

<!-- example(cdk-tree-flat) -->


In a flat tree, the hierarchy is flattened; nodes are not rendered inside of each other, but instead
are rendered as siblings in sequence. An instance of `TreeFlattener` is used to generate the flat
list of items from hierarchical data. The "level" of each tree node is read through the `getLevel`
Expand All @@ -28,6 +31,8 @@ variations, such as infinite or virtual scrolling.

#### Nested tree

<!-- example(cdk-tree-nested) -->

In nested tree, children nodes are placed inside their parent node in DOM. The parent node contains
a node outlet into which children are projected.

Expand Down
4 changes: 4 additions & 0 deletions src/material-examples/cdk-tree-flat/cdk-tree-flat-example.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.demo-tree-node {
display: flex;
align-items: center;
}
12 changes: 12 additions & 0 deletions src/material-examples/cdk-tree-flat/cdk-tree-flat-example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
<cdk-tree-node *cdkTreeNodeDef="let node" cdkTreeNodePadding class="demo-tree-node">
<button mat-icon-button disabled></button>
{{node.filename}}: {{node.type}}
</cdk-tree-node>
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding class="demo-tree-node">
<button mat-icon-button [attr.aria-label]="'toggle ' + node.filename" cdkTreeNodeToggle>
<mat-icon>{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}</mat-icon>
</button>
{{node.filename}}: {{node.type}}
</cdk-tree-node>
</cdk-tree>
162 changes: 162 additions & 0 deletions src/material-examples/cdk-tree-flat/cdk-tree-flat-example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import {Component, Injectable} from '@angular/core';
import {FlatTreeControl} from '@angular/cdk/tree';
import {MatTreeFlattener, MatTreeFlatDataSource} from '@angular/material/tree';
import {of} from 'rxjs/observable/of';
import {Observable} from 'rxjs/Observable';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';

/**
* File node data with nested structure.
* Each node has a filename, and a type or a list of children.
*/
export class FileNode {
children: FileNode[];
filename: string;
type: any;
}

/** Flat node with expandable and level information */
export class FileFlatNode {
filename: string;
type: any;
level: number;
expandable: boolean;
}

/**
* The file structure tree data in string. The data could be parsed into a Json object
*/
const TREE_DATA = `
{
"Documents": {
"angular": {
"src": {
"core": "ts",
"compiler": "ts"
}
},
"material2": {
"src": {
"button": "ts",
"checkbox": "ts",
"input": "ts"
}
}
},
"Downloads": {
"Tutorial": "html",
"November": "pdf",
"October": "pdf"
},
"Pictures": {
"Sun": "png",
"Woods": "jpg",
"Photo Booth Library": {
"Contents": "dir",
"Pictures": "dir"
}
},
"Applications": {
"Chrome": "app",
"Calendar": "app",
"Webstorm": "app"
}
}`;

/**
* File database, it can build a tree structured Json object from string.
* Each node in Json object represents a file or a directory. For a file, it has filename and type.
* For a directory, it has filename and children (a list of files or directories).
* The input will be a json object string, and the output is a list of `FileNode` with nested
* structure.
*/
@Injectable()
export class FileDatabase {
dataChange: BehaviorSubject<FileNode[]> = new BehaviorSubject<FileNode[]>([]);

get data(): FileNode[] { return this.dataChange.value; }

constructor() {
this.initialize();
}

initialize() {
// Parse the string to json object.
const dataObject = JSON.parse(TREE_DATA);

// Build the tree nodes from Json object. The result is a list of `FileNode` with nested
// file node as children.
const data = this.buildFileTree(dataObject, 0);

// Notify the change.
this.dataChange.next(data);
}

/**
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
* The return value is the list of `FileNode`.
*/
buildFileTree(value: any, level: number): FileNode[] {
let data: any[] = [];
for (let k in value) {
let v = value[k];
let node = new FileNode();
node.filename = `${k}`;
if (v === null || v === undefined) {
// no action
} else if (typeof v === 'object') {
node.children = this.buildFileTree(v, level + 1);
} else {
node.type = v;
}
data.push(node);
}
return data;
}
}

/**
* @title Tree with flat nodes
*/
@Component({
selector: 'cdk-tree-flat-example',
templateUrl: 'cdk-tree-flat-example.html',
styleUrls: ['cdk-tree-flat-example.css'],
providers: [FileDatabase]
})
export class CdkTreeFlatExample {

treeControl: FlatTreeControl<FileFlatNode>;

treeFlattener: MatTreeFlattener<FileNode, FileFlatNode>;

dataSource: MatTreeFlatDataSource<FileNode, FileFlatNode>;

constructor(database: FileDatabase) {
this.treeFlattener = new MatTreeFlattener(this.transformer, this._getLevel,
this._isExpandable, this._getChildren);
this.treeControl = new FlatTreeControl<FileFlatNode>(this._getLevel, this._isExpandable);
this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);

database.dataChange.subscribe(data => {
this.dataSource.data = data;
});
}

transformer = (node: FileNode, level: number) => {
let flatNode = new FileFlatNode();
flatNode.filename = node.filename;
flatNode.type = node.type;
flatNode.level = level;
flatNode.expandable = !!node.children;
return flatNode;
}

private _getLevel = (node: FileFlatNode) => { return node.level; };

private _isExpandable = (node: FileFlatNode) => { return node.expandable; };

private _getChildren = (node: FileNode): Observable<FileNode[]> => { return of(node.children); };

hasChild = (_: number, _nodeData: FileFlatNode) => { return _nodeData.expandable; };
}
15 changes: 15 additions & 0 deletions src/material-examples/cdk-tree-nested/cdk-tree-nested-example.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.example-tree-invisible {
display: none;
}

.example-tree ul,
.example-tree li {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}

.example-tree-node {
display: block;
padding-left: 40px;
}
15 changes: 15 additions & 0 deletions src/material-examples/cdk-tree-nested/cdk-tree-nested-example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<cdk-tree [dataSource]="nestedDataSource" [treeControl]="nestedTreeControl">
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="example-tree-node">
<button mat-icon-button disabled></button>
{{node.filename}}: {{node.type}}
</cdk-nested-tree-node>
<cdk-nested-tree-node *cdkTreeNodeDef="let node; when: hasNestedChild" class="example-tree-node">
<button mat-icon-button [attr.aria-label]="'toggle ' + node.filename" cdkTreeNodeToggle>
<mat-icon>{{nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}</mat-icon>
</button>
{{node.filename}}: {{node.type}}
<div [class.example-tree-invisible]="!nestedTreeControl.isExpanded(node)">
<ng-container cdkTreeNodeOutlet></ng-container>
</div>
</cdk-nested-tree-node>
</cdk-tree>
132 changes: 132 additions & 0 deletions src/material-examples/cdk-tree-nested/cdk-tree-nested-example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import {Component, Injectable} from '@angular/core';
import {NestedTreeControl} from '@angular/cdk/tree';
import {MatTreeNestedDataSource} from '@angular/material/tree';
import {of} from 'rxjs/observable/of';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';

/**
* Json node data with nested structure. Each node has a filename and a value or a list of children
*/
export class FileNode {
children: FileNode[];
filename: string;
type: any;
}

/**
* The Json tree data in string. The data could be parsed into Json object
*/
const TREE_DATA = `
{
"Documents": {
"angular": {
"src": {
"core": "ts",
"compiler": "ts"
}
},
"material2": {
"src": {
"button": "ts",
"checkbox": "ts",
"input": "ts"
}
}
},
"Downloads": {
"Tutorial": "html",
"November": "pdf",
"October": "pdf"
},
"Pictures": {
"Sun": "png",
"Woods": "jpg",
"Photo Booth Library": {
"Contents": "dir",
"Pictures": "dir"
}
},
"Applications": {
"Chrome": "app",
"Calendar": "app",
"Webstorm": "app"
}
}`;

/**
* File database, it can build a tree structured Json object from string.
* Each node in Json object represents a file or a directory. For a file, it has filename and type.
* For a directory, it has filename and children (a list of files or directories).
* The input will be a json object string, and the output is a list of `FileNode` with nested
* structure.
*/
@Injectable()
export class FileDatabase {
dataChange: BehaviorSubject<FileNode[]> = new BehaviorSubject<FileNode[]>([]);

get data(): FileNode[] { return this.dataChange.value; }

constructor() {
this.initialize();
}

initialize() {
// Parse the string to json object.
const dataObject = JSON.parse(TREE_DATA);

// Build the tree nodes from Json object. The result is a list of `FileNode` with nested
// file node as children.
const data = this.buildFileTree(dataObject, 0);

// Notify the change.
this.dataChange.next(data);
}

/**
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
* The return value is the list of `FileNode`.
*/
buildFileTree(value: any, level: number): FileNode[] {
let data: any[] = [];
for (let k in value) {
let v = value[k];
let node = new FileNode();
node.filename = `${k}`;
if (v === null || v === undefined) {
// no action
} else if (typeof v === 'object') {
node.children = this.buildFileTree(v, level + 1);
} else {
node.type = v;
}
data.push(node);
}
return data;
}
}

/**
* @title Tree with nested nodes
*/
@Component({
selector: 'cdk-tree-nested-example',
templateUrl: 'cdk-tree-nested-example.html',
styleUrls: ['cdk-tree-nested-example.css'],
providers: [FileDatabase]
})
export class CdkTreeNestedExample {
nestedTreeControl: NestedTreeControl<FileNode>;

nestedDataSource: MatTreeNestedDataSource<FileNode>;

constructor(database: FileDatabase) {
this.nestedTreeControl = new NestedTreeControl<FileNode>(this._getChildren);
this.nestedDataSource = new MatTreeNestedDataSource();

database.dataChange.subscribe(data => this.nestedDataSource.data = data);
}

private _getChildren = (node: FileNode) => { return of(node.children); };

hasNestedChild = (_: number, nodeData: FileNode) => {return !(nodeData.type); };
}
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
.example-tree ul,
.example-tree li {
-webkit-margin-before: 0;
-webkit-margin-after: 0;
list-style-type: none;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl" class="example-tree">
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding>
<button mat-icon-button disabled></button>
{{node.filename}} : {{node.type}}
</mat-tree-node>

Expand Down
Loading