From 907481fcae6982afb05e0c74ad7371696f027cb8 Mon Sep 17 00:00:00 2001 From: Vivian Hu Date: Thu, 20 Dec 2018 15:43:00 -0800 Subject: [PATCH] Fix(tree):allow NestedTreeControl to have null or undefined child --- src/cdk/tree/control/base-tree-control.ts | 2 +- src/cdk/tree/control/nested-tree-control.ts | 2 +- src/cdk/tree/control/tree-control.ts | 2 +- src/lib/tree/tree.spec.ts | 43 +++++++++++++++++++++ tools/public_api_guard/cdk/tree.d.ts | 8 ++-- 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/cdk/tree/control/base-tree-control.ts b/src/cdk/tree/control/base-tree-control.ts index 2525321c2aac..e1d5f3611a6a 100644 --- a/src/cdk/tree/control/base-tree-control.ts +++ b/src/cdk/tree/control/base-tree-control.ts @@ -34,7 +34,7 @@ export abstract class BaseTreeControl implements TreeControl { isExpandable: (dataNode: T) => boolean; /** Gets a stream that emits whenever the given data node's children change. */ - getChildren: (dataNode: T) => (Observable | T[]); + getChildren: (dataNode: T) => (Observable | T[] | undefined | null); /** Toggles one single data node's expanded/collapsed state. */ toggle(dataNode: T): void { diff --git a/src/cdk/tree/control/nested-tree-control.ts b/src/cdk/tree/control/nested-tree-control.ts index 319f6d73bab6..3079bdfd3770 100644 --- a/src/cdk/tree/control/nested-tree-control.ts +++ b/src/cdk/tree/control/nested-tree-control.ts @@ -13,7 +13,7 @@ import {BaseTreeControl} from './base-tree-control'; export class NestedTreeControl extends BaseTreeControl { /** Construct with nested tree function getChildren. */ - constructor(public getChildren: (dataNode: T) => (Observable | T[])) { + constructor(public getChildren: (dataNode: T) => (Observable | T[] | undefined | null)) { super(); } diff --git a/src/cdk/tree/control/tree-control.ts b/src/cdk/tree/control/tree-control.ts index 98a83d07b5b5..6eae7ab01d41 100644 --- a/src/cdk/tree/control/tree-control.ts +++ b/src/cdk/tree/control/tree-control.ts @@ -60,5 +60,5 @@ export interface TreeControl { readonly isExpandable: (dataNode: T) => boolean; /** Gets a stream that emits whenever the given data node's children change. */ - readonly getChildren: (dataNode: T) => Observable | T[]; + readonly getChildren: (dataNode: T) => Observable | T[] | undefined | null; } diff --git a/src/lib/tree/tree.spec.ts b/src/lib/tree/tree.spec.ts index 098b2ae04716..3e0322bc2c36 100644 --- a/src/lib/tree/tree.spec.ts +++ b/src/lib/tree/tree.spec.ts @@ -237,6 +237,26 @@ describe('MatTree', () => { }); }); + describe('nested tree with undefined or null children', () => { + describe('should initialize', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + configureMatTreeTestingModule([MatNestedTreeWithNullOrUndefinedChild]); + fixture = TestBed.createComponent(MatNestedTreeWithNullOrUndefinedChild); + treeElement = fixture.nativeElement.querySelector('mat-tree'); + + fixture.detectChanges(); + }); + + it('with rendered dataNodes', () => { + const nodes = getNodes(treeElement); + + expect(nodes).toBeDefined('Expect nodes to be defined'); + expect(nodes[0].classList).toContain('customNodeClass'); + }); + }); + }); describe('nested tree', () => { describe('should initialize', () => { let fixture: ComponentFixture; @@ -710,6 +730,29 @@ class MatTreeWithNullOrUndefinedChild { hasChild = (_: number, node: ExampleFlatNode) => node.expandable; } +@Component({ + template: ` + + + {{node.name}} + + + + ` +}) +class MatNestedTreeWithNullOrUndefinedChild { + treeControl: NestedTreeControl; + dataSource: MatTreeNestedDataSource; + + constructor() { + this.treeControl = new NestedTreeControl(this.getChildren); + this.dataSource = new MatTreeNestedDataSource(); + this.dataSource.data = TREE_DATA; + } + + private getChildren = (node: FoodNode) => node.children; +} + @Component({ template: ` diff --git a/tools/public_api_guard/cdk/tree.d.ts b/tools/public_api_guard/cdk/tree.d.ts index 766379e40d15..628028be6b7a 100644 --- a/tools/public_api_guard/cdk/tree.d.ts +++ b/tools/public_api_guard/cdk/tree.d.ts @@ -1,7 +1,7 @@ export declare abstract class BaseTreeControl implements TreeControl { dataNodes: T[]; expansionModel: SelectionModel; - getChildren: (dataNode: T) => (Observable | T[]); + getChildren: (dataNode: T) => (Observable | T[] | undefined | null); getLevel: (dataNode: T) => number; isExpandable: (dataNode: T) => boolean; collapse(dataNode: T): void; @@ -127,8 +127,8 @@ export declare function getTreeMultipleDefaultNodeDefsError(): Error; export declare function getTreeNoValidDataSourceError(): Error; export declare class NestedTreeControl extends BaseTreeControl { - getChildren: (dataNode: T) => (Observable | T[]); - constructor(getChildren: (dataNode: T) => (Observable | T[])); + getChildren: (dataNode: T) => (Observable | T[] | undefined | null); + constructor(getChildren: (dataNode: T) => (Observable | T[] | undefined | null)); protected _getDescendants(descendants: T[], dataNode: T): void; expandAll(): void; getDescendants(dataNode: T): T[]; @@ -137,7 +137,7 @@ export declare class NestedTreeControl extends BaseTreeControl { export interface TreeControl { dataNodes: T[]; expansionModel: SelectionModel; - readonly getChildren: (dataNode: T) => Observable | T[]; + readonly getChildren: (dataNode: T) => Observable | T[] | undefined | null; readonly getLevel: (dataNode: T) => number; readonly isExpandable: (dataNode: T) => boolean; collapse(dataNode: T): void;