@@ -40,7 +40,7 @@ import {
4040 Subject ,
4141 Subscription ,
4242} from 'rxjs' ;
43- import { concatMap , map , reduce , startWith , switchMap , take , takeUntil } from 'rxjs/operators' ;
43+ import { concatMap , map , reduce , startWith , switchMap , take , takeUntil , tap } from 'rxjs/operators' ;
4444import { TreeControl } from './control/tree-control' ;
4545import { CdkTreeNodeDef , CdkTreeNodeOutletContext } from './node' ;
4646import { CdkTreeNodeOutlet } from './outlet' ;
@@ -155,6 +155,8 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
155155 *
156156 * This controls what selection of data the tree will render.
157157 */
158+ // NB: we're unable to determine this ourselves; Angular's ContentChildren
159+ // unfortunately does not pick up the necessary information.
158160 @Input ( ) nodeType ?: 'flat' | 'nested' ;
159161
160162 // Outlets within the tree's template where the dataNodes will be inserted.
@@ -182,7 +184,11 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
182184 /** Keep track of which nodes are expanded. */
183185 private _expansionModel ?: SelectionModel < K > ;
184186
185- /** Maintain a synchronous cache of the currently known data nodes. */
187+ /**
188+ * Maintain a synchronous cache of the currently known data nodes. In the
189+ * case of nested nodes (i.e. if `nodeType` is 'nested'), this will
190+ * only contain the root nodes.
191+ */
186192 private _dataNodes : BehaviorSubject < readonly T [ ] > = new BehaviorSubject < readonly T [ ] > ( [ ] ) ;
187193
188194 /** The mapping between data and the node that is rendered. */
@@ -291,39 +297,22 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
291297 if ( dataStream ) {
292298 this . _dataSubscription = dataStream
293299 . pipe (
294- switchMap ( data => this . _flattenChildren ( data ) ) ,
300+ switchMap ( data => this . _convertChildren ( data ) ) ,
295301 takeUntil ( this . _onDestroy ) ,
296302 )
297- . subscribe ( data => this . renderNodeChanges ( data ) ) ;
303+ . subscribe ( data => this . _renderNodeChanges ( data ) ) ;
298304 } else if ( typeof ngDevMode === 'undefined' || ngDevMode ) {
299305 throw getTreeNoValidDataSourceError ( ) ;
300306 }
301307 }
302308
303309 /** Check for changes made in the data and render each change (node added/removed/moved). */
304- renderNodeChanges (
310+ _renderNodeChanges (
305311 data : readonly T [ ] ,
306312 dataDiffer : IterableDiffer < T > = this . _dataDiffer ,
307313 viewContainer : ViewContainerRef = this . _nodeOutlet . viewContainer ,
308314 parentData ?: T ,
309315 ) {
310- this . _dataNodes . next ( data ) ;
311-
312- this . _renderNodeChanges ( data , dataDiffer , viewContainer , parentData ) ;
313- }
314-
315- /** Check for changes made in the data and render each change (node added/removed/moved). */
316- _renderNodeChanges (
317- data : readonly T [ ] ,
318- dataDiffer : IterableDiffer < T > ,
319- viewContainer : ViewContainerRef ,
320- parentData ?: T ,
321- ) {
322- const levelAccessor = this . _getLevelAccessor ( ) ;
323- if ( levelAccessor && this . nodeType === 'nested' && ! parentData ) {
324- data = data . filter ( data => levelAccessor ( data ) === 0 ) ;
325- }
326-
327316 const changes = dataDiffer . diff ( data ) ;
328317 if ( ! changes ) {
329318 return ;
@@ -691,19 +680,36 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
691680 return this . expansionKey ?.( dataNode ) ?? ( dataNode as unknown as K ) ;
692681 }
693682
694- private _flattenChildren ( nodes : readonly T [ ] ) : Observable < readonly T [ ] > {
695- // If we're using TreeControl or levelAccessor, we don't need to manually
696- // flatten things here.
697- if ( ! this . childrenAccessor ) {
698- return observableOf ( nodes ) ;
699- } else {
683+ /**
684+ * Converts children for certain tree configurations. Note also that this
685+ * caches the known nodes for use in other parts of the tree.
686+ */
687+ private _convertChildren ( nodes : readonly T [ ] ) : Observable < readonly T [ ] > {
688+ // The only situations where we have to convert children types is when
689+ // they're mismatched; i.e. if the tree is using a childrenAccessor and the
690+ // nodes are flat, or if the tree is using a levelAccessor and the nodes are
691+ // nested.
692+ if ( this . childrenAccessor && this . nodeType === 'flat' ) {
693+ // This flattens children into a single array.
700694 return observableOf ( ...nodes ) . pipe (
701695 concatMap ( node => concat ( observableOf ( [ node ] ) , this . _getAllChildrenRecursively ( node ) ) ) ,
702696 reduce ( ( results , nodes ) => {
703697 results . push ( ...nodes ) ;
704698 return results ;
705699 } , [ ] as T [ ] ) ,
700+ tap ( ( nodes ) => {
701+ this . _dataNodes . next ( nodes ) ;
702+ } ) ,
706703 ) ;
704+ } else if ( this . levelAccessor && this . nodeType === 'nested' ) {
705+ this . _dataNodes . next ( nodes ) ;
706+ // In the nested case, we only look for root nodes. The CdkNestedNode
707+ // itself will handle rendering each individual node's children.
708+ const levelAccessor = this . levelAccessor ;
709+ return observableOf ( nodes . filter ( node => levelAccessor ( node ) === 0 ) ) ;
710+ } else {
711+ this . _dataNodes . next ( nodes ) ;
712+ return observableOf ( nodes ) ;
707713 }
708714 }
709715}
0 commit comments