@@ -216,6 +216,27 @@ describe('MatTree', () => {
216216 } ) ;
217217 } ) ;
218218
219+ describe ( 'flat tree with undefined or null children' , ( ) => {
220+ describe ( 'should initialize' , ( ) => {
221+ let fixture : ComponentFixture < MatTreeWithNullOrUndefinedChild > ;
222+
223+ beforeEach ( ( ) => {
224+ configureMatTreeTestingModule ( [ MatTreeWithNullOrUndefinedChild ] ) ;
225+ fixture = TestBed . createComponent ( MatTreeWithNullOrUndefinedChild ) ;
226+ treeElement = fixture . nativeElement . querySelector ( 'mat-tree' ) ;
227+
228+ fixture . detectChanges ( ) ;
229+ } ) ;
230+
231+ it ( 'with rendered dataNodes' , ( ) => {
232+ const nodes = getNodes ( treeElement ) ;
233+
234+ expect ( nodes ) . toBeDefined ( 'Expect nodes to be defined' ) ;
235+ expect ( nodes [ 0 ] . classList ) . toContain ( 'customNodeClass' ) ;
236+ } ) ;
237+ } ) ;
238+ } ) ;
239+
219240 describe ( 'nested tree' , ( ) => {
220241 describe ( 'should initialize' , ( ) => {
221242 let fixture : ComponentFixture < NestedMatTreeApp > ;
@@ -610,6 +631,85 @@ class SimpleMatTreeApp {
610631 }
611632}
612633
634+ interface FoodNode {
635+ name : string ;
636+ children ?: FoodNode [ ] | null ;
637+ }
638+
639+ /** Flat node with expandable and level information */
640+ interface ExampleFlatNode {
641+ expandable : boolean ;
642+ name : string ;
643+ level : number ;
644+ }
645+
646+ /**
647+ * Food data with nested structure.
648+ * Each node has a name and an optiona list of children.
649+ */
650+ const TREE_DATA : FoodNode [ ] = [
651+ {
652+ name : 'Fruit' ,
653+ children : [
654+ { name : 'Apple' } ,
655+ { name : 'Banana' } ,
656+ { name : 'Fruit loops' ,
657+ children : null } ,
658+ ]
659+ } , {
660+ name : 'Vegetables' ,
661+ children : [
662+ {
663+ name : 'Green' ,
664+ children : [
665+ { name : 'Broccoli' } ,
666+ { name : 'Brussel sprouts' } ,
667+ ]
668+ } , {
669+ name : 'Orange' ,
670+ children : [
671+ { name : 'Pumpkins' } ,
672+ { name : 'Carrots' } ,
673+ ]
674+ } ,
675+ ]
676+ } ,
677+ ] ;
678+
679+ @Component ( {
680+ template : `
681+ <mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
682+ <mat-tree-node *matTreeNodeDef="let node" class="customNodeClass"
683+ matTreeNodePadding matTreeNodeToggle>
684+ {{node.name}}
685+ </mat-tree-node>
686+ </mat-tree>
687+ `
688+ } )
689+ class MatTreeWithNullOrUndefinedChild {
690+ private transformer = ( node : FoodNode , level : number ) => {
691+ return {
692+ expandable : ! ! node . children ,
693+ name : node . name ,
694+ level : level ,
695+ } ;
696+ }
697+
698+ treeControl = new FlatTreeControl < ExampleFlatNode > (
699+ node => node . level , node => node . expandable ) ;
700+
701+ treeFlattener = new MatTreeFlattener (
702+ this . transformer , node => node . level , node => node . expandable , node => node . children ) ;
703+
704+ dataSource = new MatTreeFlatDataSource ( this . treeControl , this . treeFlattener , TREE_DATA ) ;
705+
706+ constructor ( ) {
707+ this . dataSource . data = TREE_DATA ;
708+ }
709+
710+ hasChild = ( _ : number , node : ExampleFlatNode ) => node . expandable ;
711+ }
712+
613713@Component ( {
614714 template : `
615715 <mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
0 commit comments