components icon indicating copy to clipboard operation
components copied to clipboard

bug(mat-tree): How to maintain tree expansion state if TreeControl is deprecated?

Open danielsharvey opened this issue 1 year ago • 8 comments

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

danielsharvey avatar Jan 01 '25 09:01 danielsharvey

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.

Sebi11 avatar Jan 15 '25 13:01 Sebi11

I agree! #29856 (comment) with ViewChild might be a solution, but we must wait for the MatTree component 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.

WwwHhhYran avatar Feb 10 '25 07:02 WwwHhhYran

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。

WwwHhhYran avatar Mar 27 '25 07:03 WwwHhhYran

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.

tutkli avatar May 14 '25 19:05 tutkli

@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?

RawRapter avatar Jun 28 '25 06:06 RawRapter

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。

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).

wesleyzloza avatar Nov 17 '25 22:11 wesleyzloza

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。

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).

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. ☹️

WwwHhhYran avatar Nov 24 '25 08:11 WwwHhhYran