@@ -29,24 +29,34 @@ import {
2929} from '@angular/core' ;
3030import {
3131 BehaviorSubject ,
32+ concat ,
3233 isObservable ,
34+ merge ,
3335 Observable ,
3436 of as observableOf ,
3537 Subject ,
3638 Subscription ,
3739} from 'rxjs' ;
38- import { takeUntil } from 'rxjs/operators' ;
40+ import { reduce , switchMap , take , takeUntil } from 'rxjs/operators' ;
3941import { TreeControl } from './control/tree-control' ;
4042import { CdkTreeNodeDef , CdkTreeNodeOutletContext } from './node' ;
4143import { CdkTreeNodeOutlet } from './outlet' ;
4244import {
4345 getMultipleTreeControlsError ,
46+ getTreeControlFunctionsMissingError ,
4447 getTreeControlMissingError ,
4548 getTreeMissingMatchingNodeDefError ,
4649 getTreeMultipleDefaultNodeDefsError ,
4750 getTreeNoValidDataSourceError ,
4851} from './tree-errors' ;
49- import { coerceNumberProperty } from '@angular/cdk/coercion' ;
52+ import { BooleanInput , coerceNumberProperty } from '@angular/cdk/coercion' ;
53+
54+ function coerceObservable < T > ( data : T | Observable < T > ) : Observable < T > {
55+ if ( ! isObservable ( data ) ) {
56+ return observableOf ( data ) ;
57+ }
58+ return data ;
59+ }
5060
5161/**
5262 * CDK tree component that connects with a data source to retrieve data of type `T` and renders
@@ -357,52 +367,111 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
357367
358368 /** Whether the data node is expanded or collapsed. Returns true if it's expanded. */
359369 isExpanded ( dataNode : T ) : boolean {
360- throw new Error ( 'not implemented' ) ;
370+ return (
371+ this . treeControl ?. isExpanded ( dataNode ) ??
372+ this . _expansionModel ?. isSelected ( this . _trackExpansionKey ( dataNode ) ) ??
373+ false
374+ ) ;
361375 }
362376
363377 /** If the data node is currently expanded, collapse it. Otherwise, expand it. */
364378 toggle ( dataNode : T ) : void {
365- throw new Error ( 'not implemented' ) ;
379+ if ( this . treeControl ) {
380+ this . treeControl . toggle ( dataNode ) ;
381+ } else if ( this . _expansionModel ) {
382+ this . _expansionModel . toggle ( this . _trackExpansionKey ( dataNode ) ) ;
383+ }
366384 }
367385
368386 /** Expand the data node. If it is already expanded, does nothing. */
369387 expand ( dataNode : T ) : void {
370- throw new Error ( 'not implemented' ) ;
388+ if ( this . treeControl ) {
389+ this . treeControl . expand ( dataNode ) ;
390+ } else if ( this . _expansionModel ) {
391+ this . _expansionModel . select ( this . _trackExpansionKey ( dataNode ) ) ;
392+ }
371393 }
372394
373395 /** Collapse the data node. If it is already collapsed, does nothing. */
374396 collapse ( dataNode : T ) : void {
375- throw new Error ( 'not implemented' ) ;
397+ if ( this . treeControl ) {
398+ this . treeControl . collapse ( dataNode ) ;
399+ } else if ( this . _expansionModel ) {
400+ this . _expansionModel . deselect ( this . _trackExpansionKey ( dataNode ) ) ;
401+ }
376402 }
377403
378404 /**
379405 * If the data node is currently expanded, collapse it and all its descendants.
380406 * Otherwise, expand it and all its descendants.
381407 */
382408 toggleDescendants ( dataNode : T ) : void {
383- throw new Error ( 'not implemented' ) ;
409+ if ( this . treeControl ) {
410+ this . treeControl . toggleDescendants ( dataNode ) ;
411+ } else if ( this . _expansionModel ) {
412+ if ( this . isExpanded ( dataNode ) ) {
413+ this . collapseDescendants ( dataNode ) ;
414+ } else {
415+ this . expandDescendants ( dataNode ) ;
416+ }
417+ }
384418 }
385419
386420 /**
387- * Expand the data node and all its descendants. If they are already expanded, does nothing.
388- */
421+ * Expand the data node and all its descendants. If they are already expanded, does nothing. */
389422 expandDescendants ( dataNode : T ) : void {
390- throw new Error ( 'not implemented' ) ;
423+ if ( this . treeControl ) {
424+ this . treeControl . expandDescendants ( dataNode ) ;
425+ } else if ( this . _expansionModel ) {
426+ const expansionModel = this . _expansionModel ;
427+ this . _getDescendants ( dataNode )
428+ . pipe ( takeUntil ( this . _onDestroy ) )
429+ . subscribe ( children => {
430+ expansionModel . select ( ...children . map ( child => this . _trackExpansionKey ( child ) ) ) ;
431+ } ) ;
432+ }
391433 }
392434
393435 /** Collapse the data node and all its descendants. If it is already collapsed, does nothing. */
394436 collapseDescendants ( dataNode : T ) : void {
395- throw new Error ( 'not implemented' ) ;
437+ if ( this . treeControl ) {
438+ this . treeControl . collapseDescendants ( dataNode ) ;
439+ } else if ( this . _expansionModel ) {
440+ const expansionModel = this . _expansionModel ;
441+ this . _getDescendants ( dataNode )
442+ . pipe ( takeUntil ( this . _onDestroy ) )
443+ . subscribe ( children => {
444+ expansionModel . deselect ( ...children . map ( child => this . _trackExpansionKey ( child ) ) ) ;
445+ } ) ;
446+ }
396447 }
397448
398449 /** Expands all data nodes in the tree. */
399450 expandAll ( ) : void {
400- throw new Error ( 'not implemented' ) ;
451+ if ( this . treeControl ) {
452+ this . treeControl . expandAll ( ) ;
453+ } else if ( this . _expansionModel ) {
454+ const expansionModel = this . _expansionModel ;
455+ this . _getAllDescendants ( )
456+ . pipe ( takeUntil ( this . _onDestroy ) )
457+ . subscribe ( children => {
458+ expansionModel . select ( ...children . map ( child => this . _trackExpansionKey ( child ) ) ) ;
459+ } ) ;
460+ }
401461 }
402462
403463 /** Collapse all data nodes in the tree. */
404464 collapseAll ( ) : void {
405- throw new Error ( 'not implemented' ) ;
465+ if ( this . treeControl ) {
466+ this . treeControl . collapseAll ( ) ;
467+ } else if ( this . _expansionModel ) {
468+ const expansionModel = this . _expansionModel ;
469+ this . _getAllDescendants ( )
470+ . pipe ( takeUntil ( this . _onDestroy ) )
471+ . subscribe ( children => {
472+ expansionModel . deselect ( ...children . map ( child => this . _trackExpansionKey ( child ) ) ) ;
473+ } ) ;
474+ }
406475 }
407476
408477 /** Level accessor, used for compatibility between the old Tree and new Tree */
@@ -414,6 +483,72 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
414483 _getChildrenAccessor ( ) {
415484 return this . treeControl ?. getChildren ?? this . childrenAccessor ;
416485 }
486+
487+ private _getAllDescendants ( ) : Observable < T [ ] > {
488+ return merge ( ...( this . _dataNodes ?. map ( dataNode => this . _getDescendants ( dataNode ) ) ?? [ ] ) ) ;
489+ }
490+
491+ private _getDescendants ( dataNode : T ) : Observable < T [ ] > {
492+ if ( this . treeControl ) {
493+ return observableOf ( this . treeControl . getDescendants ( dataNode ) ) ;
494+ }
495+ if ( this . levelAccessor ) {
496+ // If we have no known nodes, we wouldn't be able to determine descendants
497+ if ( ! this . _dataNodes ) {
498+ return observableOf ( [ ] ) ;
499+ }
500+ const startIndex = this . _dataNodes . indexOf ( dataNode ) ;
501+ const results : T [ ] = [ dataNode ] ;
502+
503+ // Goes through flattened tree nodes in the `dataNodes` array, and get all descendants.
504+ // The level of descendants of a tree node must be greater than the level of the given
505+ // tree node.
506+ // If we reach a node whose level is equal to the level of the tree node, we hit a sibling.
507+ // If we reach a node whose level is greater than the level of the tree node, we hit a
508+ // sibling of an ancestor.
509+ const currentLevel = this . levelAccessor ( dataNode ) ;
510+ for (
511+ let i = startIndex + 1 ;
512+ i < this . _dataNodes . length && currentLevel < this . levelAccessor ( this . _dataNodes [ i ] ) ;
513+ i ++
514+ ) {
515+ results . push ( this . _dataNodes [ i ] ) ;
516+ }
517+ return observableOf ( results ) ;
518+ }
519+ if ( this . childrenAccessor ) {
520+ return this . _getChildrenRecursively ( dataNode ) . pipe (
521+ reduce (
522+ ( memo : T [ ] , next ) => {
523+ memo . push ( ...next ) ;
524+ return memo ;
525+ } ,
526+ [ dataNode ] ,
527+ ) ,
528+ ) ;
529+ }
530+ throw getTreeControlMissingError ( ) ;
531+ }
532+
533+ private _getChildrenRecursively ( dataNode : T ) : Observable < T [ ] > {
534+ if ( ! this . childrenAccessor ) {
535+ return observableOf ( [ ] ) ;
536+ }
537+
538+ return coerceObservable ( this . childrenAccessor ( dataNode ) ) . pipe (
539+ take ( 1 ) ,
540+ switchMap ( children => {
541+ return concat (
542+ observableOf ( children ) ,
543+ ...children . map ( child => this . _getChildrenRecursively ( child ) ) ,
544+ ) ;
545+ } ) ,
546+ ) ;
547+ }
548+
549+ private _trackExpansionKey ( dataNode : T ) : K {
550+ return this . expansionKey ?.( dataNode ) ?? ( dataNode as unknown as K ) ;
551+ }
417552}
418553
419554/**
0 commit comments