@@ -53,7 +53,6 @@ import {
5353 inject ,
5454 booleanAttribute ,
5555 TemplateRef ,
56- OnChanges ,
5756} from '@angular/core' ;
5857import { toObservable , toSignal } from '@angular/core/rxjs-interop' ;
5958import { 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