components icon indicating copy to clipboard operation
components copied to clipboard

Use of custom TrackByFunction in MatTree does not preserve expansion state when nodes change

Open takamori opened this issue 5 years ago • 4 comments

What is the expected behavior?

Use of trackBy should also ensure that the expansion state does not get lost when nodes are updated.

What is the current behavior?

Use of trackBy does not ensure that the expansion state does not get lost when nodes are updated. This is presumably since TreeControl uses an expansionModel which is a SelectionModel<T> where the SelectionModel has no awareness of the TrackByFunction.

What are the steps to reproduce?

Relying upon the TreeControl expansionModel: https://stackblitz.com/edit/angular-5wpr9o-kjnmsn?file=app%2Ftree-flat-overview-example.ts

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

all

Is there anything else we should know?

Trying to store expansion state in the nodes themselves as a workaround doesn't work (unless you transfer the state): https://stackblitz.com/edit/angular-5wpr9o-ahptjs?file=app%2Ftree-flat-overview-example.ts

Having a custom expansionModel that is aware of the trackBy state works to preserve state, since the TreeControl's expansion-related methods apparently aren't actually used internally by the tree component: https://stackblitz.com/edit/angular-5wpr9o-pbmk1g?file=app%2Ftree-flat-overview-example.ts

takamori avatar Apr 22 '19 17:04 takamori

I spent some time looking into it, but at least with the way things are set up at the moment, there's no nice way to solve it. We can pass the trackBy function to the tree control and have it use the function when tracking which nodes are expanded, but the problem is that the tree control doesn't know the indexes of the items so it can't call the trackBy function correctly. We do have access to the array of all the data nodes from which we can figure out the index, but we'd have to do it very frequently (multiple times per change detection) which would defeat the purpose of a trackBy a bit.

crisbeto avatar Jun 29 '19 08:06 crisbeto

Any updates on this one? I'd love to add a trackBy on our app's tree and improve performance. It's a big tree and trackBy could give some big performance gains.

Using the expansion model mostly worked but fails intermittently after calling this.treeControl.collapseAll() or this.treeControl.expandAll(). (Despite looping through treeControl.dataNodes and adding all node IDs to the expansionModel, or calling expansionModel.clear()).

inorganik avatar Feb 12 '20 23:02 inorganik

Oh man, I so wish trackBy worked with the expansion state! Without this I'm stuck with paying the performance cost of re-expanding the tree on every change to the dataSource. For even moderately sized trees this can be expensive:

    this.treeControl.expansionModel.select(
      ...this.treeControl.dataNodes.filter((node) => node.expanded)
    );

Has there been any update to this? I see the OP posted a workaround; I will reference that and see if it works me.

dlmoffett avatar Oct 05 '21 00:10 dlmoffett

Unfortunately this problem means I can't use Mat-Tree. Fortunately, Angular makes it very easy to make your own tree out of a ul and a recursive component.

wgibbons1 avatar Sep 24 '22 08:09 wgibbons1