I am implementing the angular/material tree component and having some problems. After saving changes to the tree I am resetting the data backing the tree (which works) however the app then becomes incredibly slow, expanding nodes can take about 8 seconds.
What makes this even weirder is that the code that actually manipulates the data source is run in other places, such as adding a new child to the tree - we want the UI to update, and this does not cause a problem. It is only when saving that the app becomes slow.
save(): void {
const trees = this.flattenedTree.filter(node => this.isRootNode(node));
this.serviceThatHasNoSideEffects.save(trees)
.then(result => {
this.tree = result;
this.flattenedTree = this.getFlattenedTree();
this.refreshTree();
});
}
private refreshTree() {
const data: any[] = this.flattenedTree
.filter(x => this.isRootNode(x))
.filter(x => x.children.length > 0 || x.id === this.focusId);
this.nestedDataSource.data = null;
this.nestedDataSource.data = data;
const focusNode = this.getNode(this.focusId);
this.nestedTreeControl.expand(focusNode);
}
private addChild(parentNode: any, childNode: any) {
if (!this.getNode(parentNode)) {
this.flattenedTree.push(parentNode);
}
if (!this.getNode(childNode)) {
this.flattenedTree.push(childNode);
}
parentNode.children.push(childNode);
this.refreshTree();
this.nestedTreeControl.expand(parentNode);
}
EDIT:
changing refresh tree to create a completely new data source solves the slow issue (memory leak?) but not adding a child isnt displaying in the UI. Although the child is there on the flattened tree so should be displayed.
private refreshTree() {
const data: any[] = this.flattenedTree
.filter(x => this.isRootNode(x))
.filter(x => x.children.length > 0 || x.id === this.focusId);
this.nestedDataSource = new MatTreeNestedDataSource<theTreeType>();
this.nestedDataSource.data = data;
const focusNode = this.getNode(this.focusId);
this.nestedTreeControl.expand(focusNode);
}
EDIT: here is the html backing it. pretty standard.
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
<li class="mat-tree-node">
<button mat-icon-button disabled></button>
{{node.uniqueName}}
</li>
</mat-tree-node>
<!--when has nested child-->
<mat-nested-tree-node *matTreeNodeDef="let node; when: hasNestedChild">
<li>
<div class="mat-tree-node">
<button mat-icon-button matTreeNodeToggle>
<mat-icon>
{{nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>
{{node.uniqueName}}
</div>
<ul [class.invisible]="!nestedTreeControl.isExpanded(node)">
<ng-container matTreeNodeOutlet></ng-container>
</ul>
</li>
</mat-nested-tree-node>
</mat-tree>
Your app is rendering too often. Let's start with this quite common issue: your application re-renders components unnecessarily, making your application slower than it could be. This is both easy to solve and easy to cause.
Many Angular developers are frustrated with Angular because they say it is slow. Some think that it's hard to build a fast Angular application. Especially given that the bundle sizes of its biggest rivals - React & Vue. js - are usually about half as small and take about half the time to parse and run the JavaScript.
The <mat-tree> directive is used to create trees in Angular. The tree is a type of structure that contains data organized in a hierarchical way. Each data item is represented by a node of the tree. The Angular Material Tree is an enhancement over the previous structure, Component Dev Kit Tree (cdk-tree).
Although the accepted answer seems to offer some speed improvement, the solution that finally did it for me (in Angular 8) is this:
https://stackoverflow.com/a/59655114/134120
Change the line from the official examples:
<ul [class.example-tree-invisible]="!treeControl.isExpanded(node)">
to this:
<ul *ngIf="treeControl.isExpanded(node)">
so that collapsed subtrees are not loaded in the DOM at all.
it took me a few hours to get it working but here is the change I made:
// cdk tree that mat tree is based on has a bug causing children to not be rendered in the UI without first setting the data to null
this.nestedDataSource.data = null;
// mat-tree has some sort of memory leak issue when not instantiating a new MatTreeNestedDataSource which causes the app to become very slow
this.nestedDataSource = new MatTreeNestedDataSource<LocationHierarchyNodeDataModel>();
this.nestedDataSource.data = data;
The displaying children issue I found here: https://github.com/angular/material2/issues/11381
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With