@@ -41,6 +41,7 @@ import {
4141 Output ,
4242 QueryList ,
4343 TrackByFunction ,
44+ computed ,
4445 signal ,
4546 effect ,
4647 ViewChild ,
@@ -50,7 +51,7 @@ import {
5051 inject ,
5152 booleanAttribute ,
5253} from '@angular/core' ;
53- import { toSignal } from '@angular/core/rxjs-interop' ;
54+ import { toObservable , toSignal } from '@angular/core/rxjs-interop' ;
5455import { coerceObservable } from '@angular/cdk/coercion/private' ;
5556import {
5657 BehaviorSubject ,
@@ -85,6 +86,7 @@ import {
8586 getTreeMultipleDefaultNodeDefsError ,
8687 getTreeNoValidDataSourceError ,
8788} from './tree-errors' ;
89+ import { NgTemplateOutlet } from '@angular/common' ;
8890
8991type RenderingData < T > =
9092 | {
@@ -105,7 +107,16 @@ type RenderingData<T> =
105107@Component ( {
106108 selector : 'cdk-tree' ,
107109 exportAs : 'cdkTree' ,
108- template : `<ng-container cdkTreeNodeOutlet></ng-container>` ,
110+ template : `
111+ <ng-template let-nodes>
112+ @for (node of nodes(); track node.key) {
113+ <ng-container
114+ [ngTemplateOutlet]="node.template"
115+ [ngTemplateOutletContext]="node.context" />
116+ }
117+ </ng-template>
118+ <ng-container cdkTreeNodeOutlet></ng-container>
119+ ` ,
109120 host : {
110121 'class' : 'cdk-tree' ,
111122 'role' : 'tree' ,
@@ -118,7 +129,7 @@ type RenderingData<T> =
118129 // tslint:disable-next-line:validate-decorators
119130 changeDetection : ChangeDetectionStrategy . Default ,
120131 standalone : true ,
121- imports : [ CdkTreeNodeOutlet ] ,
132+ imports : [ CdkTreeNodeOutlet , NgTemplateOutlet ] ,
122133} )
123134export class CdkTree < T , K = T >
124135 implements
@@ -261,13 +272,6 @@ export class CdkTree<T, K = T>
261272 /** Keep track of which nodes are expanded. */
262273 private _expansionModel ?: SelectionModel < K > ;
263274
264- /**
265- * Maintain a synchronous cache of flattened data nodes. This will only be
266- * populated after initial render, and in certain cases, will be delayed due to
267- * relying on Observable `getChildren` calls.
268- */
269- private _flattenedNodes : BehaviorSubject < readonly T [ ] > = new BehaviorSubject < readonly T [ ] > ( [ ] ) ;
270-
271275 /** The automatically determined node type for the tree. */
272276 private _nodeType = new BehaviorSubject < 'flat' | 'nested' | null > ( null ) ;
273277
@@ -297,6 +301,18 @@ export class CdkTree<T, K = T>
297301 ) ,
298302 { initialValue : null } ,
299303 ) ;
304+ /**
305+ * Maintain a synchronous cache of flattened data nodes. This will only be
306+ * populated after initial render, and in certain cases, will be delayed due to
307+ * relying on Observable `getChildren` calls.
308+ */
309+ private readonly _flattenedNodes = computed ( ( ) => {
310+ return this . _renderData ( ) ?. flattenedNodes ?? [ ] ;
311+ } ) ;
312+ private readonly _flatNodesObs = toObservable ( this . _flattenedNodes ) ;
313+ private readonly _renderNodes = computed ( ( ) => {
314+ return this . _renderData ( ) ?. renderNodes ?? [ ] ;
315+ } ) ;
300316
301317 constructor ( ...args : unknown [ ] ) ;
302318 constructor ( ) {
@@ -404,7 +420,6 @@ export class CdkTree<T, K = T>
404420
405421 // If we're here, then we know what our node type is, and therefore can
406422 // perform our usual rendering pipeline.
407- this . _updateCachedData ( data . flattenedNodes ) ;
408423 this . renderNodeChanges ( data . renderNodes ) ;
409424 this . _updateKeyManagerItems ( data . flattenedNodes ) ;
410425 // Explicitly detect the initial set of changes to this component subtree
@@ -686,7 +701,7 @@ export class CdkTree<T, K = T>
686701 } else if ( this . _expansionModel ) {
687702 const expansionModel = this . _expansionModel ;
688703 expansionModel . select (
689- ...this . _flattenedNodes . value . map ( child => this . _getExpansionKey ( child ) ) ,
704+ ...this . _flattenedNodes ( ) . map ( child => this . _getExpansionKey ( child ) ) ,
690705 ) ;
691706 }
692707 }
@@ -698,7 +713,7 @@ export class CdkTree<T, K = T>
698713 } else if ( this . _expansionModel ) {
699714 const expansionModel = this . _expansionModel ;
700715 expansionModel . deselect (
701- ...this . _flattenedNodes . value . map ( child => this . _getExpansionKey ( child ) ) ,
716+ ...this . _flattenedNodes ( ) . map ( child => this . _getExpansionKey ( child ) ) ,
702717 ) ;
703718 }
704719 }
@@ -739,15 +754,14 @@ export class CdkTree<T, K = T>
739754 ) ;
740755
741756 if ( levelAccessor ) {
742- return combineLatest ( [ isExpanded , this . _flattenedNodes ] ) . pipe (
757+ return combineLatest ( [ isExpanded , this . _flatNodesObs ] ) . pipe (
743758 map ( ( [ expanded , flattenedNodes ] ) => {
744759 if ( ! expanded ) {
745760 return [ ] ;
746761 }
747762 return this . _findChildrenByLevel (
748763 levelAccessor ,
749764 flattenedNodes ,
750-
751765 dataNode ,
752766 1 ,
753767 ) ;
@@ -878,7 +892,7 @@ export class CdkTree<T, K = T>
878892 if ( this . levelAccessor ) {
879893 const results = this . _findChildrenByLevel (
880894 this . levelAccessor ,
881- this . _flattenedNodes . value ,
895+ this . _flattenedNodes ( ) ,
882896 dataNode ,
883897 Infinity ,
884898 ) ;
@@ -1083,10 +1097,6 @@ export class CdkTree<T, K = T>
10831097 }
10841098 }
10851099
1086- private _updateCachedData ( flattenedNodes : readonly T [ ] ) {
1087- this . _flattenedNodes . next ( flattenedNodes ) ;
1088- }
1089-
10901100 private _updateKeyManagerItems ( flattenedNodes : readonly T [ ] ) {
10911101 this . _keyManagerNodes . next ( flattenedNodes ) ;
10921102 }
0 commit comments