fix(tree,scrolling): don't require user DataSource to extend DataSource (#14966)

Currently the way we verify that the passed in data source is by using `instanceof` which assumes that the consumer has extended our `DataSource`. In `cdk/table` we do the verification by checking whether the object matches the interface of a `DataSource` instead.

These changes add a utility method to check whether something is a data source and refactor the `tree` and `scrolling` entry points to be consistent with `table`.
This commit is contained in:
Kristiyan Kostadinov
2019-01-30 04:29:33 +01:00
committed by mmalerba
parent 64010d785b
commit 0ab81138cd
5 changed files with 26 additions and 16 deletions
+8
View File
@@ -29,3 +29,11 @@ export abstract class DataSource<T> {
*/
abstract disconnect(collectionViewer: CollectionViewer): void;
}
/** Checks whether an object is a data source. */
export function isDataSource(value: any): value is DataSource<any> {
// Check if the value is a DataSource by observing if it has a connect function. Cannot
// be checked as an `instanceof DataSource` since people could create their own sources
// that match the interface, but don't extend DataSource.
return value && typeof value.connect === 'function';
}
+8 -2
View File
@@ -6,7 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ArrayDataSource, CollectionViewer, DataSource, ListRange} from '@angular/cdk/collections';
import {
ArrayDataSource,
CollectionViewer,
DataSource,
ListRange,
isDataSource,
} from '@angular/cdk/collections';
import {
Directive,
DoCheck,
@@ -82,7 +88,7 @@ export class CdkVirtualForOf<T> implements CollectionViewer, DoCheck, OnDestroy
}
set cdkVirtualForOf(value: DataSource<T> | Observable<T[]> | NgIterable<T>) {
this._cdkVirtualForOf = value;
const ds = value instanceof DataSource ? value :
const ds = isDataSource(value) ? value :
// Slice the value if its an NgIterable to ensure we're working with an array.
new ArrayDataSource<T>(
value instanceof Observable ? value : Array.prototype.slice.call(value || []));
+5 -9
View File
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CollectionViewer, DataSource} from '@angular/cdk/collections';
import {CollectionViewer, DataSource, isDataSource} from '@angular/cdk/collections';
import {
AfterContentChecked,
Attribute,
@@ -449,7 +449,7 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
this._onDestroy.next();
this._onDestroy.complete();
if (this.dataSource instanceof DataSource) {
if (isDataSource(this.dataSource)) {
this.dataSource.disconnect(this);
}
}
@@ -769,7 +769,7 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
private _switchDataSource(dataSource: CdkTableDataSourceInput<T>) {
this._data = [];
if (this.dataSource instanceof DataSource) {
if (isDataSource(this.dataSource)) {
this.dataSource.disconnect(this);
}
@@ -796,12 +796,8 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
let dataStream: Observable<T[] | ReadonlyArray<T>> | undefined;
// Check if the datasource is a DataSource object by observing if it has a connect function.
// Cannot check this.dataSource['connect'] due to potential property renaming, nor can it
// checked as an instanceof DataSource<T> since the table should allow for data sources
// that did not explicitly extend DataSource<T>.
if ((this.dataSource as DataSource<T>).connect instanceof Function) {
dataStream = (this.dataSource as DataSource<T>).connect(this);
if (isDataSource(this.dataSource)) {
dataStream = this.dataSource.connect(this);
} else if (this.dataSource instanceof Observable) {
dataStream = this.dataSource;
} else if (Array.isArray(this.dataSource)) {
+3 -5
View File
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {FocusableOption} from '@angular/cdk/a11y';
import {CollectionViewer, DataSource} from '@angular/cdk/collections';
import {CollectionViewer, DataSource, isDataSource} from '@angular/cdk/collections';
import {
AfterContentChecked,
ChangeDetectionStrategy,
@@ -185,10 +185,8 @@ export class CdkTree<T>
private _observeRenderChanges() {
let dataStream: Observable<T[] | ReadonlyArray<T>> | undefined;
// Cannot use `instanceof DataSource` since the data source could be a literal with
// `connect` function and may not extends DataSource.
if (typeof (this._dataSource as DataSource<T>).connect === 'function') {
dataStream = (this._dataSource as DataSource<T>).connect(this);
if (isDataSource(this._dataSource)) {
dataStream = this._dataSource.connect(this);
} else if (this._dataSource instanceof Observable) {
dataStream = this._dataSource;
} else if (Array.isArray(this._dataSource)) {
+2
View File
@@ -15,6 +15,8 @@ export declare abstract class DataSource<T> {
export declare function getMultipleValuesInSingleSelectionError(): Error;
export declare function isDataSource(value: any): value is DataSource<any>;
export declare type ListRange = {
start: number;
end: number;