When using mat-nested-tree-node with a nested datasource if you add a child to an existing parent it does not update the view.
Well if you change or add a child to an existing parent in the nestedDataSource it should update the tree.
It does not update the view when adding a child to an existing parent to the nestedDataStructure .
Well in my case i need to update the view since i add/delete nodes .
versions 6 (Chrome ver 66)
I found an workaround:
refreshTree(){
let _data = this.nestedDataSource.data;
this.nestedDataSource.data = null;
this.nestedDataSource.data = _data;
}
Can you please provide a reproduction in stackblitz
I have met the same issue. Could anyone please help confirm whether its a bug?
this.dataChange.next(data);
When update with new object ( e.g data = [] or a new instance), it works.
When push/update 'data' object, tree view will not be re render, though this.nestedDataSource.data already updated.
I think is a bug since it works in one case but not in the other one ! Feel free to use my workaround , meanwhile 馃憤 .
refreshTree(){
let _data = this.nestedDataSource.data;
this.nestedDataSource.data = null;
this.nestedDataSource.data = _data;
}
I think the cdk-tree (the basis of mat-tree) has the same issue, see here:
https://stackblitz.com/edit/cdk-nested-tree?file=src%2Fapp%2Fmytree%2Fmytree.component.ts
clicking the 'add' button should push a new node onto the children array. console shows it does, but the tree is not updated.
it also does not update the tree node, if we update the node.
https://stackblitz.com/angular/onaqbrpjaan
saving a new label to the node does not update the tree.
Have the same issue. Any updates?
I've had the same issue.
Appears that binding and UI reflecting works only for the first layer of the tree nodes, but not for any nodes down further. Glad to see there is a workaround, thanks to @juggernut
Any updates for this? I must use refresh method from @juggernut to refresh the datasource
@juggernut's fix seems only work for the data source but treeControl still fails, the toggle doesn't work after updating the data.
Closed because it was fixed? If we have to use the workaround it should be documented before the issue is closed, right?
Sorry i am just stupid ... closed by mistake :)
Any update on this? or estimation when should be fixed?
Is a P4 problem (considered low priority ) i dont have the time to see if i can do something in the source code . If i have the time i will try to make a pull request my self . But dont hold your breath ( i am really busy ) . Hopefully someone better than me gets to this problem before me !
@juggernut's fix seems only work for the data source but treeControl still fails, the toggle doesn't work after updating the data.
Yeah I reloaded the datasource but after I am unable to get the indexOf a node using below
treeControl.dataNodes.indexOf( node )
Above returns -1
This problem should be considered higher priority. The workaround introduces quite a serious performance issues for big trees. But it is currently the only way how to implement the asynchronous loading of the nodes
Any updates on this? I am about to implement a dynamically loaded folder menu and I wanted to use the nested data source/ nested tree control, but judging by this thread it seems I'm better off using the flat data source?
@vpatov sadly there is currently no progress at it seems. Did you try using trackBy from mat-tree ? I didn't test it but if its working for child items, it will improve the performance when resetting the whole tree source...
But thats not the solution i know, and it depends on your usecase.
Update:
I tested the trackBy and it works for all items in the tree. This mean you can use this as workaround combined with resetting the source (as meantioned already).
In case the tracked => same items will not be rerendered,
1) the performance is better
2) expand / collapsed nodes will keep their state.
Update 2:
Sorry i was to fast with my comment, and i tested one of my old custom implemenations of mat-tree, which handles the trackBy stuff a bit different. Its a long time ago i implemented that tree but i thought i used the default behavior from mat-tree, which was not the case....
While creating a quick example in stackblitz (with the default mat-tree) i realized no matter if you use trackBy nodes will be rerenderd.
but trackBy combined with the "source to null workaround" is NOT improving any rendering performance. As in the mat-tree docu trackBy only helps to improve performance for treeOperations....
Anyway: here is the example (thats exactly the workaround the TO wrote, nothing new and not clean..):
https://stackblitz.com/edit/angular-kc77e4
sorry for spreading hope
@Priemar add some code example , it helps people :) .
@Priemar Thank you for your efforts, I am just using the workaround for now because it works fine for me, and doesn't seem to have a strong performance impact.
Any update on this? We are needing this behavior as well.
For me, it DOES updates the view (I used the example "Tree with checkboxes" from here), but when adding a node to a LEAF node, it doesn't refreshing the tree well, as it doesn't present the EXPAND button for the leaf node (which is no leaf at all after adding a node under it).
The WORKAROUND for me was to save aside root's node when initializing the tree, and then in every node addition, call this method:
private refreshTree(): void {
this.treeControl.collapse(this.rootTree);
this.treeControl.expand(this.rootTree);
}
This causing the root tree to get collapsed and get expanded again, which builds the view properly after the changes.
The cdk tree only renders updates of the backing datastore when the IterableDiffer
sees a difference, https://github.com/angular/components/blob/master/src/cdk/tree/tree.ts#L215.
Therefore tree updates are not triggered when objects references stays the same. In order to solve that properly, all objects from the root to the relevant updated node must be updated, aka an immutable update https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns/
Would be nice if the documentation mentioned that, and in addition, had an example of an update.
Here's an example of removing / adding nodes to a nested data source. It replaces relevant nodes on the path from the root to the changed node. And it keeps the expansion state.
https://angular-material-nested-tree-with-updates.stackblitz.io/
Br. Jakob
Hi Jakob,
Thanks for the info. Could you please share the stack blitz that contains the code for you example?
Kind Regards
Could you please share the stack blitz that contains the code for you example?
Here it is:
https://stackblitz.com/edit/angular-material-nested-tree-with-updates
@adrianriepl @jakobadam
Having Ngrx & immutable js in reducer - job is perfectly done, and additional state cloning here is kind of defeating the purpose of immutable js, I think? Also I have 'normalized' model to help minimize cloning of a graph. The list of children is just an ids, and there is a Map of nodes in a state to navigate by id. I've been so excided by resultant performance of this approach, till I noticed that nested tree is barely works.
Is there a way to actually change IterableDiffer, or add some external hints, or manually trigger renderNodeChanges? The thing is - I'm aware about every change because reducer just did the job. I think I can even add the hint into state itself or add a side effect (ngrx effect) with hints, but I don't want to waste resources and let IterableDiffer or sometihng to actually find what was changed in the tree, this sounds like extra dummy work if reducer have this knowledge. May be any ideas?
UPD: I just found some inspiration here!
2021 still open, since so i bump, cuz iam having the described problem. will check the "workarounds"
I've got the same issue too. Using MatTreeFlatDataSource I'm able to add new nodes to existing parents but when adding to a leaf node the changes are not being detected. I haven't had much luck with the workarounds either. Any other ideas?
Most helpful comment
I think is a bug since it works in one case but not in the other one ! Feel free to use my workaround , meanwhile 馃憤 .
refreshTree(){
let _data = this.nestedDataSource.data;
this.nestedDataSource.data = null;
this.nestedDataSource.data = _data;
}