Skip to content

Commit 6d94aec

Browse files
tinayuangaojosephperrott
authored andcommitted
fix(tree): fix type error in tree (#10095)
1 parent 19463bb commit 6d94aec

File tree

6 files changed

+86
-13
lines changed

6 files changed

+86
-13
lines changed

src/cdk/tree/nested-node.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {takeUntil} from 'rxjs/operators/takeUntil';
1717

1818
import {CdkTree, CdkTreeNode} from './tree';
1919
import {CdkTreeNodeOutlet} from './outlet';
20+
import {getTreeControlFunctionsMissingError} from './tree-errors';
2021

2122
/**
2223
* Nested node is a child of `<cdk-tree>`. It works with nested tree.
@@ -62,6 +63,9 @@ export class CdkNestedTreeNode<T> extends CdkTreeNode<T> implements AfterContent
6263
}
6364

6465
ngAfterContentInit() {
66+
if (!this._tree.treeControl.getChildren) {
67+
throw getTreeControlFunctionsMissingError();
68+
}
6569
this._tree.treeControl.getChildren(this.data).pipe(takeUntil(this._destroyed))
6670
.subscribe(result => {
6771
if (result && result.length) {

src/cdk/tree/tree.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ import {Observable} from 'rxjs/Observable';
1414
import {combineLatest} from 'rxjs/observable/combineLatest';
1515
import {map} from 'rxjs/operators/map';
1616

17+
import {BaseTreeControl} from './control/base-tree-control';
1718
import {TreeControl} from './control/tree-control';
1819
import {FlatTreeControl} from './control/flat-tree-control';
1920
import {NestedTreeControl} from './control/nested-tree-control';
2021
import {CdkTreeModule} from './index';
2122
import {CdkTree} from './tree';
23+
import {getTreeControlFunctionsMissingError} from './tree-errors';
2224

2325

2426
describe('CdkTree', () => {
@@ -587,6 +589,18 @@ describe('CdkTree', () => {
587589
[`[topping_3] - [cheese_3] + [base_3]`]);
588590
});
589591
});
592+
593+
it('should throw an error when missing function in nested tree', () => {
594+
configureCdkTreeTestingModule([NestedCdkErrorTreeApp]);
595+
expect(() => TestBed.createComponent(NestedCdkErrorTreeApp).detectChanges())
596+
.toThrowError(getTreeControlFunctionsMissingError().message);
597+
});
598+
599+
it('should throw an error when missing function in flat tree', () => {
600+
configureCdkTreeTestingModule([FlatCdkErrorTreeApp]);
601+
expect(() => TestBed.createComponent(FlatCdkErrorTreeApp).detectChanges())
602+
.toThrowError(getTreeControlFunctionsMissingError().message);
603+
});
590604
});
591605
});
592606

@@ -994,3 +1008,56 @@ class ObservableDataSourceNestedCdkTreeApp {
9941008
@ViewChild(CdkTree) tree: CdkTree<TestData>;
9951009
}
9961010

1011+
@Component({
1012+
template: `
1013+
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
1014+
<cdk-nested-tree-node *cdkTreeNodeDef="let node" class="customNodeClass">
1015+
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
1016+
<ng-template cdkTreeNodeOutlet></ng-template>
1017+
</cdk-nested-tree-node>
1018+
</cdk-tree>
1019+
`
1020+
})
1021+
class NestedCdkErrorTreeApp {
1022+
getLevel = (node: TestData) => node.level;
1023+
1024+
isExpandable = (node: TestData) => node.children.length > 0;
1025+
1026+
treeControl: TreeControl<TestData> = new FlatTreeControl(this.getLevel, this.isExpandable);
1027+
1028+
dataSource: FakeDataSource | null = new FakeDataSource(this.treeControl);
1029+
1030+
@ViewChild(CdkTree) tree: CdkTree<TestData>;
1031+
}
1032+
1033+
class FakeTreeControl extends BaseTreeControl<TestData> {
1034+
getDescendants(_: TestData): TestData[] {
1035+
return this.dataNodes;
1036+
}
1037+
1038+
expandAll(): void {
1039+
// No op
1040+
}
1041+
}
1042+
@Component({
1043+
template: `
1044+
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
1045+
<cdk-tree-node *cdkTreeNodeDef="let node" class="customNodeClass">
1046+
{{node.pizzaTopping}} - {{node.pizzaCheese}} + {{node.pizzaBase}}
1047+
<ng-template cdkTreeNodeOutlet></ng-template>
1048+
</cdk-tree-node>
1049+
</cdk-tree>
1050+
`
1051+
})
1052+
class FlatCdkErrorTreeApp {
1053+
1054+
getLevel = (node: TestData) => node.level;
1055+
1056+
isExpandable = (node: TestData) => node.children.length > 0;
1057+
1058+
treeControl: TreeControl<TestData> = new FakeTreeControl();
1059+
1060+
dataSource: FakeDataSource | null = new FakeDataSource(this.treeControl);
1061+
1062+
@ViewChild(CdkTree) tree: CdkTree<TestData>;
1063+
}

src/cdk/tree/tree.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class CdkTreeNode<T> implements FocusableOption, OnDestroy {
6060
* The most recently created `CdkTreeNode`. We save it in static variable so we can retrieve it
6161
* in `CdkTree` and set the data to it.
6262
*/
63-
static mostRecentTreeNode: CdkTreeNode<any> | null = null;
63+
static mostRecentTreeNode: CdkTreeNode<{}> | null = null;
6464

6565
/** Subject that emits when the component has been destroyed. */
6666
protected _destroyed = new Subject<void>();
@@ -89,7 +89,7 @@ export class CdkTreeNode<T> implements FocusableOption, OnDestroy {
8989

9090
constructor(protected _elementRef: ElementRef,
9191
protected _tree: CdkTree<T>) {
92-
CdkTreeNode.mostRecentTreeNode = this;
92+
CdkTreeNode.mostRecentTreeNode = this as CdkTreeNode<T>;
9393
}
9494

9595
ngOnDestroy() {
@@ -140,7 +140,7 @@ export class CdkTree<T> implements CollectionViewer, OnInit, OnDestroy {
140140
private _onDestroy = new Subject<void>();
141141

142142
/** Latest data provided by the data source through the connect interface. */
143-
private _data: Array<T> = new Array<T>();
143+
private _data = new Array<T>();
144144

145145
/** Differ used to find the changes in the data provided by the data source. */
146146
private _dataDiffer: IterableDiffer<T>;
@@ -304,7 +304,7 @@ export class CdkTree<T> implements CollectionViewer, OnInit, OnDestroy {
304304
* definition.
305305
*/
306306
_getNodeDef(data: T, i: number): CdkTreeNodeDef<T> {
307-
if (this._nodeDefs.length == 1) { return this._nodeDefs.first; }
307+
if (this._nodeDefs.length === 1) { return this._nodeDefs.first; }
308308

309309
const nodeDef =
310310
this._nodeDefs.find(def => def.when && def.when(i, data)) || this._defaultNodeDef;
@@ -321,7 +321,7 @@ export class CdkTree<T> implements CollectionViewer, OnInit, OnDestroy {
321321
const node = this._getNodeDef(nodeData, index);
322322

323323
// Node context that will be provided to created embedded view
324-
const context: CdkTreeNodeOutletContext<T> = new CdkTreeNodeOutletContext<T>(nodeData);
324+
const context = new CdkTreeNodeOutletContext<T>(nodeData);
325325

326326
// Use default tree nodeOutlet, or nested node's nodeOutlet
327327
const container = viewContainer ? viewContainer : this._nodeOutlet.viewContainer;

src/demo-app/tree/checklist-tree-demo/checklist-tree-demo.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {SelectionModel} from '@angular/cdk/collections';
1111
import {FlatTreeControl} from '@angular/cdk/tree';
1212
import {MatTreeFlattener, MatTreeFlatDataSource} from '@angular/material/tree';
1313
import {of as ofObservable} from 'rxjs/observable/of';
14+
import {Observable} from 'rxjs/Observable';
1415
import {TodoItemNode, TodoItemFlatNode, ChecklistDatabase} from './checklist-database';
1516

1617
/**
@@ -61,7 +62,9 @@ export class ChecklistTreeDemo {
6162

6263
isExpandable = (node: TodoItemFlatNode) => { return node.expandable; };
6364

64-
getChildren = (node: TodoItemNode) => { return ofObservable(node.children); };
65+
getChildren = (node: TodoItemNode): Observable<TodoItemNode[]> => {
66+
return ofObservable(node.children);
67+
}
6568

6669
hasChild = (_: number, _nodeData: TodoItemFlatNode) => { return _nodeData.expandable; };
6770

src/demo-app/tree/dynamic-tree-demo/dynamic-database.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {Injectable} from '@angular/core';
99
import {FlatTreeControl} from '@angular/cdk/tree';
1010
import {CollectionViewer, SelectionChange} from '@angular/cdk/collections';
1111
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
12+
import {Observable} from 'rxjs/Observable';
1213
import {merge} from 'rxjs/observable/merge';
1314
import {map} from 'rxjs/operators/map';
1415

@@ -67,12 +68,9 @@ export class DynamicDataSource {
6768
constructor(private treeControl: FlatTreeControl<DynamicFlatNode>,
6869
private database: DynamicDatabase) {}
6970

70-
connect(collectionViewer: CollectionViewer) {
71-
const changes = [
72-
collectionViewer.viewChange,
73-
this.treeControl.expansionModel.onChange!,
74-
];
75-
return merge(...changes).pipe(map((change) => {
71+
connect(collectionViewer: CollectionViewer): Observable<DynamicFlatNode[]> {
72+
return merge(collectionViewer.viewChange, this.treeControl.expansionModel.onChange!)
73+
.pipe(map((change) => {
7674
if ((change as SelectionChange<DynamicFlatNode>).added ||
7775
(change as SelectionChange<DynamicFlatNode>).removed) {
7876
this.handleTreeControl(change as SelectionChange<DynamicFlatNode>);

src/demo-app/tree/loadmore-tree-demo/loadmore-tree-demo.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {Component} from '@angular/core';
1010
import {FlatTreeControl} from '@angular/cdk/tree';
1111
import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
12+
import {Observable} from 'rxjs/Observable';
1213
import {LoadmoreNode, LoadmoreFlatNode, LoadmoreDatabase} from './loadmore-database';
1314

1415
const LOAD_MORE = 'LOAD_MORE';
@@ -50,7 +51,7 @@ export class LoadmoreTreeDemo {
5051
database.initialize();
5152
}
5253

53-
getChildren = (node: LoadmoreNode) => { return node.childrenChange; };
54+
getChildren = (node: LoadmoreNode): Observable<LoadmoreNode[]> => { return node.childrenChange; };
5455

5556
transformer = (node: LoadmoreNode, level: number) => {
5657
if (this.nodeMap.has(node.item)) {

0 commit comments

Comments
 (0)