bug(mat-tree): How to maintain tree expansion state if TreeControl is deprecated?
Is this a regression?
- [x] Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
pre v18.2.0
Description
Previously, it was possible to maintain the expansion state independent of the MatTree (rendered) component by using a TreeControl.
With TreeControl deprecated, this does not appear possible any longer.
I rely on this feature so would appreciate consideration of a path forward.
Of note, the selection model is maintained privately in MatTree: https://github.com/angular/components/blob/b961966d285c1f4986d214084ff585a713adc3bd/src/cdk/tree/tree.ts#L366-L372
Note that this is not a duplicate of #29856, which looks to relate to accessing some of the methods of TreeControl, which have been made available on MatTree itself.
Reproduction
I can produce a reproduction but I believe the regression has been clearly explained above and a reproduction does not add value.
Expected Behavior
There is a mechanism for maintaining expansion state independent of the rendered component.
Actual Behavior
Expansion state is lost when the component is destroyed.
Environment
Angular CLI: 18.0.3
Node: 18.20.5
Package Manager: npm 10.8.2
OS: darwin arm64
Angular: 18.0.2
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.1800.3
@angular-devkit/build-angular 18.0.3
@angular-devkit/core 18.0.3
@angular-devkit/schematics 18.0.3
@angular/cdk 18.2.14
@angular/cli 18.0.3
@angular/flex-layout 14.0.0-beta.41
@angular/material 18.2.14
@angular/material-luxon-adapter 18.2.14
@schematics/angular 18.0.3
ng-packagr 18.0.0
rxjs 7.8.1
typescript 5.4.5
zone.js 0.14.4
I agree!
https://github.com/angular/components/issues/29856#issuecomment-2453220960 with ViewChild might be a solution, but we must wait for the MatTree component to be rendered before calling methods on it.
I agree! #29856 (comment) with
ViewChildmight be a solution, but we must wait for theMatTreecomponent to be rendered before calling methods on it.
That's true 👍, but I feel that deprecating TreeControl has made the control of trees even more complex.
Execuse, any one can address this issue ? @crisbeto @andrewseguin @@BobobUnicorn
I think the original TreeControl was able to ensure the isolation of the view from the model, which made it easier for us to manage the data and the state of the tree. But nowadays it's only possible to manage it through tree instances which is obviously not convenient. Let me give you an example:
In the old version (before v 18.2.0), I would pass only some of the data to be rendered to the tree view, and all the data to the Tree Control, and I could manage the nodes through the tree control's expansionModel. if I need to expand some other nodes, I can do it through the tree control's expand method and combine node isExpanded status to pass the certain node data to the tree.
But now, if I use levelAccessor property, I have to pass all the data to the tree, and then use style.display combined with the shouldRender method to hide some nodes that shouldn't be shown, like the official demo. If I don't pass all the data to the tree, it may cause methods like expandAll, expandDescendants, etc. to be incorrect. I dont think this is an elegant approach, and this approach may lead to inconvenient use。
Before I used TreeControl to mix virtual scrolling with a tree. Similar to this example, but with the deprecation of these classes it would be impossible in the future. Rendering huge trees will be very expensive performance wise.
@crisbeto Following the deprecation of TreeControl, alternative ways to programmatically expand and collapse the entire tree (like expandAll() and collapseAll()) would be beneficial. Are there plans to introduce such methods directly on mat-tree, or is there a recommended pattern for achieving this without TreeControl, or do we have to go with a custom implementation?
But now, if I use
levelAccessorproperty, I have to pass all the data to the tree, and then usestyle.displaycombined with theshouldRendermethod to hide some nodes that shouldn't be shown, like the official demo. If I don't pass all the data to the tree, it may cause methods likeexpandAll,expandDescendants, etc. to be incorrect. I dont think this is an elegant approach, and this approach may lead to inconvenient use。
I recently refactored a tree component to use the new levelAccessor and had the same issue with managing state. I am using a context menu to provide controls for expanding/collapsing all descendants of a node. It took some time to realize that I couldn't rely on a isExpanded property to know the nodes state. Instead I had to request the state from the tree, e.g., tree.isExpanded(node).
But now, if I use
levelAccessorproperty, I have to pass all the data to the tree, and then usestyle.displaycombined with theshouldRendermethod to hide some nodes that shouldn't be shown, like the official demo. If I don't pass all the data to the tree, it may cause methods likeexpandAll,expandDescendants, etc. to be incorrect. I dont think this is an elegant approach, and this approach may lead to inconvenient use。I recently refactored a tree component to use the new
levelAccessorand had the same issue with managing state. I am using a context menu to provide controls for expanding/collapsing all descendants of a node. It took some time to realize that I couldn't rely on aisExpandedproperty to know the nodes state. Instead I had to request the state from the tree, e.g.,tree.isExpanded(node).
I have raised an issue https://github.com/angular/components/issues/30735 in the community and it has been confirmed as a bug, but there are no contributors to fix this issue. So, I tried to fix this issue myself https://github.com/angular/components/pull/31039. Similarly, no one helped me with the review for half a year. ☹️