From ebdc873e987ab1772562d5e81e3424e00be7d2b3 Mon Sep 17 00:00:00 2001 From: vivian-hu <41269630+vivian-hu@users.noreply.github.com> Date: Thu, 3 Jan 2019 13:56:35 -0800 Subject: [PATCH] Fix(tree):allow NestedTreeControl to have null or undefined child (#14601) --- 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 2525321c2aa..e1d5f3611a6 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 319f6d73bab..3079bdfd377 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 98a83d07b5b..6eae7ab01d4 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 098b2ae0471..3e0322bc2c3 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 766379e40d1..628028be6b7 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;