Skip to content

Commit 64406c1

Browse files
committed
refactor(cdk/tree): signalify the trackBy/expansionKey functions
1 parent b0d4ee1 commit 64406c1

File tree

2 files changed

+27
-18
lines changed

2 files changed

+27
-18
lines changed

src/cdk/tree/nested-node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class CdkNestedTreeNode<T, K = T>
6666
}
6767

6868
ngAfterContentInit() {
69-
this._dataDiffer = this._differs.find([]).create(this._tree.trackBy);
69+
this._dataDiffer = this._differs.find([]).create(this._tree._trackByFn());
7070
this._tree
7171
._getDirectChildren(this.data)
7272
.pipe(takeUntil(this._destroyed))

src/cdk/tree/tree.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ import {
5353
inject,
5454
booleanAttribute,
5555
TemplateRef,
56-
OnChanges,
5756
} from '@angular/core';
5857
import {toObservable, toSignal} from '@angular/core/rxjs-interop';
5958
import {coerceObservable} from '@angular/cdk/coercion/private';
@@ -129,10 +128,10 @@ class NodeOutletTemplateContext<T, K> {
129128
},
130129
],
131130
})
132-
export class CdkTreeNodeRenderer<T, K> implements OnChanges {
131+
export class CdkTreeNodeRenderer<T, K> implements OnInit {
133132
readonly node = input.required<TemplateParams<T, K>>();
134133

135-
ngOnChanges() {
134+
ngOnInit() {
136135
if (CdkTreeNode.mostRecentTreeNode) {
137136
CdkTreeNode.mostRecentTreeNode.data = this.node().context.$implicit;
138137
}
@@ -294,12 +293,12 @@ export class CdkTree<T, K = T>
294293
* relative to the function to know if a node should be added/removed/moved.
295294
* Accepts a function that takes two parameters, `index` and `item`.
296295
*/
297-
@Input() trackBy: TrackByFunction<T>;
296+
readonly trackBy = input<TrackByFunction<T>>();
298297

299298
/**
300299
* Given a data node, determines the key by which we determine whether or not this node is expanded.
301300
*/
302-
@Input() expansionKey?: (dataNode: T) => K;
301+
readonly expansionKey = input<(dataNode: T) => K>();
303302

304303
// Outlets within the tree's template where the dataNodes will be inserted.
305304
@ViewChild(CdkTreeNodeOutlet, {static: true}) _nodeOutlet: CdkTreeNodeOutlet;
@@ -349,6 +348,24 @@ export class CdkTree<T, K = T>
349348
_keyManager: TreeKeyManagerStrategy<CdkTreeNode<T, K>>;
350349
private _viewInit = false;
351350

351+
private readonly _expansionKeyFn = computed(() => {
352+
// In the case that a key accessor function was not provided by the
353+
// tree user, we'll default to using the node object itself as the key.
354+
//
355+
// This cast is safe since:
356+
// - if an expansionKey is provided, TS will infer the type of K to be
357+
// the return type.
358+
// - if it's not, then K will be defaulted to T.
359+
return this.expansionKey() ?? ((item: T) => (item as unknown as K));
360+
});
361+
readonly _trackByFn = computed(() => {
362+
const trackBy = this.trackBy();
363+
if (trackBy) return trackBy;
364+
const expansionKey = this._expansionKeyFn();
365+
// Provide a default trackBy based on `_ExpansionKey` if one isn't provided.
366+
return (_index: number, item: T) => expansionKey(item);
367+
});
368+
352369
private readonly _renderData = toSignal(
353370
combineLatest([this._transformedDataSource, this._nodeType, this._selection]).pipe(
354371
switchMap(([data, nodeType, expandedKeys]) =>
@@ -369,6 +386,7 @@ export class CdkTree<T, K = T>
369386
private readonly _renderNodes = computed((): Array<TemplateParams<T, K>> => {
370387
const nodes = this._renderData()?.renderNodes ?? [];
371388
const levelAccessor = this._getLevelAccessor();
389+
const trackBy = this._trackByFn();
372390

373391
return nodes.map((nodeData, index) => {
374392
const node = this._getNodeDef(nodeData, index);
@@ -389,7 +407,7 @@ export class CdkTree<T, K = T>
389407
return {
390408
context,
391409
template: node.template,
392-
key,
410+
key: trackBy(index, nodeData),
393411
};
394412
});
395413
});
@@ -549,9 +567,7 @@ export class CdkTree<T, K = T>
549567
}
550568

551569
private _initializeDataDiffer() {
552-
// Provide a default trackBy based on `_getExpansionKey` if one isn't provided.
553-
const trackBy = this.trackBy ?? ((_index: number, item: T) => this._getExpansionKey(item));
554-
this._dataDiffer = this._differs.find([]).create(trackBy);
570+
this._dataDiffer = this._differs.find([]).create(this._trackByFn());
555571
}
556572

557573
private _checkTreeControlUsage() {
@@ -1009,14 +1025,7 @@ export class CdkTree<T, K = T>
10091025
}
10101026

10111027
private _getExpansionKey(dataNode: T): K {
1012-
// In the case that a key accessor function was not provided by the
1013-
// tree user, we'll default to using the node object itself as the key.
1014-
//
1015-
// This cast is safe since:
1016-
// - if an expansionKey is provided, TS will infer the type of K to be
1017-
// the return type.
1018-
// - if it's not, then K will be defaulted to T.
1019-
return this.expansionKey?.(dataNode) ?? (dataNode as unknown as K);
1028+
return this._expansionKeyFn()(dataNode);
10201029
}
10211030

10221031
private _getAriaSet(node: T) {

0 commit comments

Comments
 (0)