Created
October 23, 2018 10:37
-
-
Save thapld/c18537de07037e759d612a240754c568 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {ResourceService} from '../../services/resource.service'; | |
import {FlatTreeControl} from '@angular/cdk/tree'; | |
import {ResourceNode} from '../resource-node'; | |
import {BehaviorSubject, merge, Observable} from 'rxjs'; | |
import {CollectionViewer, SelectionChange} from '@angular/cdk/collections'; | |
import {map} from 'rxjs/operators'; | |
import {ResourceTree} from '../resource-tree'; | |
export class ResourcesTreeDatasource { | |
dataChange: BehaviorSubject<ResourceNode[]> = new BehaviorSubject<ResourceNode[]>([]); | |
constructor(private resourceService: ResourceService, | |
private treeControl: FlatTreeControl<ResourceNode>) { | |
} | |
initialData() { | |
this.resourceService.findAllParent().subscribe( | |
value => { | |
const rns: ResourceNode[] = []; | |
const rm: ResourceTree[] = value; | |
for (let i = 0; i < rm.length; i++) { | |
const rn = new ResourceNode(); | |
rn.title = rm[i].title; | |
rn.status = rm[i].status; | |
rn.type = rm[i].type; | |
rn.id = rm[i].id; | |
rn.level = 0; | |
rn.expandable = rm[i].childrenCount > 0; | |
rns.push(rn); | |
} | |
this.data = rns; | |
}, | |
error => { | |
console.log(error); | |
} | |
); | |
} | |
get data(): ResourceNode[] { | |
return this.dataChange.value; | |
} | |
set data(value: ResourceNode[]) { | |
this.treeControl.dataNodes = value; | |
this.dataChange.next(value); | |
} | |
connect(collectionViewer: CollectionViewer): Observable<ResourceNode[]> { | |
this.treeControl.expansionModel.changed.subscribe(change => { | |
if ((change as SelectionChange<ResourceNode>).added || | |
(change as SelectionChange<ResourceNode>).removed) { | |
this.handleTreeControl(change as SelectionChange<ResourceNode>); | |
} | |
}); | |
return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data)); | |
} | |
/** Handle expand/collapse behaviors */ | |
handleTreeControl(change: SelectionChange<ResourceNode>) { | |
if (change.added) { | |
change.added.forEach((node) => this.toggleNode(node, true)); | |
} | |
if (change.removed) { | |
change.removed.reverse().forEach((node) => this.toggleNode(node, false)); | |
} | |
} | |
/** | |
* Toggle the node, remove from display list | |
*/ | |
toggleNode(node: ResourceNode, expand: boolean) { | |
node.isLoading = true; | |
const index = this.data.indexOf(node); | |
if (index < 0) { | |
node.isLoading = false; | |
return; | |
} | |
if (expand) { | |
//Avoid call db | |
if(node.item && node.item.length > 0) { | |
this.data.splice(index + 1, 0, ...node.item); | |
node.isLoading = false; | |
this.dataChange.next(this.data); | |
}else { | |
this.resourceService.findAllChildByParent(node.id) | |
.pipe( | |
map(value => { | |
return value.map(rm => { | |
const rn = new ResourceNode(); | |
rn.title = rm.title; | |
rn.status = rm.status; | |
rn.type = rm.type; | |
rn.level = node.level + 1; | |
rn.id = rm.id; | |
rn.expandable = rm.childrenCount > 0; | |
return rn; | |
}); | |
}) | |
) | |
.subscribe( | |
children => { | |
if (!children) { | |
return; | |
} | |
node.item = children; | |
this.data.splice(index + 1, 0, ...children); | |
node.isLoading = false; | |
this.dataChange.next(this.data); | |
}, error => { | |
console.log(error); | |
node.isLoading = false; | |
this.dataChange.next(this.data); | |
} | |
); | |
} | |
} else { | |
let count = 0; | |
for (let i = index + 1; i < this.data.length; i++) { | |
if (this.data[i].level > node.level) { | |
count++; | |
} | |
} | |
this.data.splice(index + 1, count); | |
node.isLoading = false; | |
this.dataChange.next(this.data); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h3>Tree Resource component</h3> | |
<div> | |
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl"> | |
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding> | |
<button mat-icon-button disabled></button> | |
<mat-checkbox class="checklist-leaf-node" | |
[checked]="checklistSelection.isSelected(node)" | |
(change)="checklistSelection.toggle(node)">{{node.title}} | |
</mat-checkbox> | |
</mat-tree-node> | |
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding> | |
<button mat-icon-button matTreeNodeToggle | |
[attr.aria-label]="'toggle ' + node.title"> | |
<mat-icon class="mat-icon-rtl-mirror"> | |
{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}} | |
</mat-icon> | |
</button> | |
<mat-checkbox | |
[checked]="descendantsAllSelected(node)" | |
[indeterminate]="descendantsPartiallySelected(node)" | |
(change)="resourceItemSelectionToggle(node)">{{node.title}} | |
</mat-checkbox> | |
<mat-progress-bar *ngIf="node.isLoading" | |
mode="indeterminate" | |
class="tree-progress-bar"> | |
</mat-progress-bar> | |
</mat-tree-node> | |
</mat-tree> | |
<pre> | |
{{checklistSelection.selected | json}} | |
</pre> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Component, OnInit} from '@angular/core'; | |
import {ResourceService} from '../../_core/services/resource.service'; | |
import {FlatTreeControl} from '@angular/cdk/tree'; | |
import {ResourceNode} from '../../_core/models/resource-node'; | |
import {ResourcesTreeDatasource} from '../../_core/models/data-sources/resources-tree.datasource'; | |
import {SelectionModel} from '@angular/cdk/collections'; | |
@Component({ | |
selector: 'm-test-component', | |
templateUrl: './test-component.component.html', | |
styleUrls: ['./test-component.component.scss'], | |
}) | |
export class TestComponentComponent implements OnInit { | |
treeControl: FlatTreeControl<ResourceNode>; | |
dataSource: ResourcesTreeDatasource; | |
checklistSelection = new SelectionModel<ResourceNode>(false); | |
constructor(private resourceService: ResourceService) { | |
this.treeControl = new FlatTreeControl<ResourceNode>(this.getLevel, this.isExpandable); | |
this.dataSource = new ResourcesTreeDatasource(this.resourceService, this.treeControl); | |
this.dataSource.initialData(); | |
} | |
ngOnInit(): void { | |
} | |
getLevel = (node: ResourceNode) => { | |
return node.level; | |
}; | |
isExpandable = (node: ResourceNode) => { | |
return node.expandable; | |
}; | |
hasChild = (_: number, _nodeData: ResourceNode) => { | |
return _nodeData.expandable; | |
}; | |
/** Whether all the descendants of the node are selected */ | |
descendantsAllSelected(node: ResourceNode): boolean { | |
const descendants = this.treeControl.getDescendants(node); | |
return descendants.every(child => { | |
return this.checklistSelection.isSelected(child); | |
}); | |
} | |
/** Whether part of the descendants are selected */ | |
descendantsPartiallySelected(node: ResourceNode): boolean { | |
const descendants = this.treeControl.getDescendants(node); | |
const result = descendants.some(child => { | |
return this.checklistSelection.isSelected(child) | |
}); | |
return result && !this.descendantsAllSelected(node); | |
} | |
/** Toggle the to-do item selection. Select/deselect all the descendants node */ | |
resourceItemSelectionToggle(node: ResourceNode): void { | |
this.checklistSelection.toggle(node); | |
const descendants = this.treeControl.getDescendants(node); | |
this.checklistSelection.isSelected(node) | |
? this.checklistSelection.select(...descendants) | |
: this.checklistSelection.deselect(...descendants); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment