-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Description
Hi
I'm using mat-tree material angular with checkboxes and dynamic data which comes from a http call service. I have two problems now:
1- all root nodes of tree are checked at the beginning! and select and deselect nodes and parent nodes does not work correctly
2- toggling parent nodes does not work fine either because there may be more that one level deeper in a parent node (which I,m trying to collapse) but only first level will be collapsed according to the count of its child. what is the solution? I even used a recursive function to handle that but it's not working fine too.
I would be grateful to help me with.
here are my codes:
view:
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl" dir="rtl"> <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding> <button mat-icon-button disabled></button> <mat-checkbox class="checklist-leaf-node" [indeterminate]="descendantsPartiallySelected(node)" [checked]="checklistSelection.isSelected(node)" (change)="todoLeafItemSelectionToggle(node);"> {{node.Title}} </mat-checkbox> <mat-progress-bar *ngIf="node.isLoading" mode="indeterminate" class="example-tree-progress-bar"></mat-progress-bar> </mat-tree-node> <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding> <button mat-icon-button matTreeNodeToggle [attr.aria-label]="'toggle ' + node.filename"> <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)="todoItemSelectionToggle(node)">{{node.Title}}</mat-checkbox> <mat-progress-bar *ngIf="node.isLoading" mode="indeterminate" class="example-tree-progress-bar"></mat-progress-bar> </mat-tree-node> </mat-tree>
my component:
` import { CollectionViewer, SelectionChange, SelectionModel } from '@angular/cdk/collections';
import { FlatTreeControl } from '@angular/cdk/tree';
import { Component, Injectable } from '@angular/core';
import { Service } from '../service/callajaxandhttp/service.component';
import { BehaviorSubject, Observable, merge } from 'rxjs';
import { map } from 'rxjs/operators';
export class DynamicFlatNode {
constructor(
public Title: string,
public id: string,
public PID: number,
public Type: string,
public s: string,
public haschildren: boolean,
public level: number = 1,
public expandable: boolean = false,
public isLoading: boolean = false,
public childcount: number = 0,
public children: DynamicFlatNode[] = []
) { }
}
@Injectable()
export class DynamicDatabase {
dfns: DynamicFlatNode[];
Tempchilddfns: DynamicFlatNode[];
public getData(url: string, ptitle: string, parentnode: string): Promise {
ptitle = ptitle != '' ? '?ptitle=' + ptitle : '';
parentnode = parentnode != '' ? '?parent=' + parentnode : '';
return this.service.callservice('get', url + ptitle + parentnode, "").then(
(response: any) =>
response)
.catch(error => console.log(error));
}
constructor(private service: Service) {
}
}
@Injectable()
export class DynamicDataSource {
dataChange: BehaviorSubject<DynamicFlatNode[]> = new BehaviorSubject<DynamicFlatNode[]>([]);
get data(): DynamicFlatNode[] { return this.dataChange.value; }
set data(value: DynamicFlatNode[]) {
this.treeControl.dataNodes = value;
this.dataChange.next(value);
}
constructor(private treeControl: FlatTreeControl,
private database: DynamicDatabase) { }
connect(collectionViewer: CollectionViewer): Observable<DynamicFlatNode[]> {
this.treeControl.expansionModel.onChange!.subscribe(change => {
if ((change as SelectionChange).added ||
(change as SelectionChange).removed) {
this.handleTreeControl(change as SelectionChange);
}
});
return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data));
}
handleTreeControl(change: SelectionChange) {
if (change.added) {
change.added.forEach((node) => this.toggleNode(node, true));
}
if (change.removed) {
change.removed.reverse().forEach((node) => this.toggleNode(node, false));
}
}
toggleNode(node: DynamicFlatNode, expand: boolean) {
const index = this.data.indexOf(node);
if (expand) {
if (node.level == 3) return;
node.isLoading = true;
if (node.children.length == 0) {
const children = this.database.getData('Subject/scripts/Subjectw.ashx?parent=' + node.id, '', "").then(
(response: any) => {
const nodes = response.map(
ContentNode =>
new DynamicFlatNode(
ContentNode.Title,
ContentNode.id,
ContentNode.PID,
ContentNode.Type,
ContentNode.s,
ContentNode.children,
node.level + 1,
ContentNode.children))
if (!children || index < 0) { // If no children, or cannot find the node, no op
return;
}
node.childcount = nodes.length;
node.children = nodes;
this.data.splice(index + 1, 0, ...nodes);
// notify the change
this.dataChange.next(this.data);
node.isLoading = false;
})
.catch(error => console.log(error));
}
else {
this.data.splice(index + 1, 0, ...node.children);
this.dataChange.next(this.data);
node.isLoading = false;
}
} else {
this.data.splice(index + 1, node.childcount,);//children.length
this.dataChange.next(this.data);
}
}
}
@component({
selector: 'Contenttree',
templateUrl: './contenttree.component.html',
styleUrls: ['./contenttree.component.css'],
providers: [DynamicDatabase]
})
export class ContenttreeComponent {
RootContents: DynamicFlatNode[] = [];
constructor(database: DynamicDatabase, private service: Service) {
this.treeControl = new FlatTreeControl(this.getLevel, this.isExpandable);
this.dataSource = new DynamicDataSource(this.treeControl, database);
database.getData('Subject/scripts/Subjectw.ashx', '', '').then(
(response: any) =>
this.dataSource.data = response.map(
ContentNode =>
new DynamicFlatNode(
ContentNode.Title,
ContentNode.id,
ContentNode.PID,
ContentNode.Type,
ContentNode.s,
ContentNode.children,
0,
ContentNode.children))
)
.catch(error => console.log(error));
}
dataSource: DynamicDataSource;
treeControl: FlatTreeControl;
getLevel = (node: DynamicFlatNode) => { return node.level; };
isExpandable = (node: DynamicFlatNode) => { return node.expandable; };
hasChild = (_: number, _nodeData: DynamicFlatNode) => { return nodeData.expandable; };
hasNoContent = (: number, _nodeData: DynamicFlatNode) => { return _nodeData.Title === ''; };
checklistSelection = new SelectionModel(true /* multiple */);
/** Whether all the descendants of the node are selected. */
descendantsAllSelected(node: DynamicFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);
const descAllSelected = descendants.every(child =>
this.checklistSelection.isSelected(child)
);
return descAllSelected;
}
/** Whether part of the descendants are selected */
descendantsPartiallySelected(node: DynamicFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);
const result = descendants.some(child => this.checklistSelection.isSelected(child));
return result && !this.descendantsAllSelected(node);
}
/** Toggle the to-do item selection. Select/deselect all the descendants node */
todoItemSelectionToggle(node: DynamicFlatNode): void {
this.checklistSelection.toggle(node);
const descendants = this.treeControl.getDescendants(node);
this.checklistSelection.isSelected(node)
? this.checklistSelection.select(...descendants)
: this.checklistSelection.deselect(...descendants);
// Force update for the parent
descendants.every(child =>
this.checklistSelection.isSelected(child)
);
this.checkAllParentsSelection(node);
}
/** Toggle a leaf to-do item selection. Check all the parents to see if they changed */
todoLeafItemSelectionToggle(node: DynamicFlatNode): void {
this.checklistSelection.toggle(node);
this.checkAllParentsSelection(node);
}
/* Checks all the parents when a leaf node is selected/unselected */
checkAllParentsSelection(node: DynamicFlatNode): void {
let parent: DynamicFlatNode | null = this.getParentNode(node);
while (parent) {
this.checkRootNodeSelection(parent);
parent = this.getParentNode(parent);
}
}
/** Check root node checked state and change it accordingly */
checkRootNodeSelection(node: DynamicFlatNode): void {
const nodeSelected = this.checklistSelection.isSelected(node);
const descendants = this.treeControl.getDescendants(node);
const descAllSelected = descendants.every(child =>
this.checklistSelection.isSelected(child)
);
if (nodeSelected && !descAllSelected) {
this.checklistSelection.deselect(node);
} else if (!nodeSelected && descAllSelected) {
this.checklistSelection.select(node);
}
}
/* Get the parent node of a node */
getParentNode(node: DynamicFlatNode): DynamicFlatNode | null {
const currentLevel = this.getLevel(node);
if (currentLevel < 1) {
return null;
}
const startIndex = this.treeControl.dataNodes.indexOf(node) - 1;
for (let i = startIndex; i >= 0; i--) {
const currentNode = this.treeControl.dataNodes[i];
if (this.getLevel(currentNode) < currentLevel) {
return currentNode;
}
}
return null;
}
}
`
thanks alot