Skip to content

Commit a9600e7

Browse files
authored
feat(table): add MatTableDataSource (#6747)
* feat(table): add MdTableDataSource * change license
1 parent 2361983 commit a9600e7

File tree

9 files changed

+657
-80
lines changed

9 files changed

+657
-80
lines changed

src/cdk/rxjs/rx-operators.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Observable, ObservableInput} from 'rxjs/Observable';
9+
// Subscribable needs to be imported because combineLatest has or is using it, but it does not need
10+
// to be used in any of the explicit typings in this file.
11+
// tslint:disable-next-line:no-unused-variable
12+
import {Observable, ObservableInput, Subscribable} from 'rxjs/Observable';
1013
import {PartialObserver} from 'rxjs/Observer';
1114
import {Subscription} from 'rxjs/Subscription';
1215
import {IScheduler} from 'rxjs/Scheduler';
@@ -22,6 +25,7 @@ import {startWith as startWithOperator} from 'rxjs/operator/startWith';
2225
import {debounceTime as debounceTimeOperator} from 'rxjs/operator/debounceTime';
2326
import {auditTime as auditTimeOperator} from 'rxjs/operator/auditTime';
2427
import {takeUntil as takeUntilOperator} from 'rxjs/operator/takeUntil';
28+
import {combineLatest as combineLatestOperator} from 'rxjs/operator/combineLatest';
2529
import {delay as delayOperator} from 'rxjs/operator/delay';
2630

2731
/**
@@ -38,16 +42,16 @@ import {delay as delayOperator} from 'rxjs/operator/delay';
3842
*/
3943
export interface StrictRxChain<T> {
4044
call<R>(operator: mapOperatorType<T, R>,
41-
project: (value: T, index: number) => R, thisArg?: any): StrictRxChain<R>;
45+
project: (value: T, index: number) => R, thisArg?: any): StrictRxChain<R>;
4246

4347
call<R>(operator: switchMapOperatorType<T, R>,
44-
project: (value: T, index: number) => ObservableInput<R>): StrictRxChain<R>;
48+
project: (value: T, index: number) => ObservableInput<R>): StrictRxChain<R>;
4549

4650
call<R>(operator: catchOperatorType<T, R>,
47-
selector: (err: any, caught: Observable<T>) => ObservableInput<R>): StrictRxChain<T | R>;
51+
selector: (err: any, caught: Observable<T>) => ObservableInput<R>): StrictRxChain<T | R>;
4852

4953
call(operator: filterOperatorType<T>,
50-
predicate: (value: T, index: number) => boolean, thisArg?: any): StrictRxChain<T>;
54+
predicate: (value: T, index: number) => boolean, thisArg?: any): StrictRxChain<T>;
5155

5256
call(operator: shareOperatorType<T>): StrictRxChain<T>;
5357

@@ -65,15 +69,18 @@ export interface StrictRxChain<T> {
6569
call(operator: startWithOperatorType<T>, ...args: any[]): StrictRxChain<T>;
6670

6771
call(operator: debounceTimeOperatorType<T>, dueTime: number,
68-
scheduler?: IScheduler): StrictRxChain<T>;
72+
scheduler?: IScheduler): StrictRxChain<T>;
6973

7074
call(operator: auditTimeOperatorType<T>, duration: number,
71-
scheduler?: IScheduler): StrictRxChain<T>;
75+
scheduler?: IScheduler): StrictRxChain<T>;
7276

7377
call(operator: takeUntilOperatorType<T>, notifier: Observable<any>): StrictRxChain<T>;
7478

79+
call<T2>(operator: combineLatestOperatorType<T, T2>,
80+
v2: ObservableInput<T2>): StrictRxChain<[T, T2]>;
81+
7582
call(operator: delayOperatorType<T>, delay: number | Date, scheduler?: IScheduler):
76-
StrictRxChain<T>;
83+
StrictRxChain<T>;
7784

7885
subscribe(fn: (t: T) => void): Subscription;
7986

@@ -93,6 +100,7 @@ export class StartWithBrand { private _; }
93100
export class DebounceTimeBrand { private _; }
94101
export class AuditTimeBrand { private _; }
95102
export class TakeUntilBrand { private _; }
103+
export class CombineLatestBrand { private _; }
96104
export class DelayBrand { private _; }
97105
/* tslint:enable:no-unused-variable */
98106

@@ -109,9 +117,10 @@ export type startWithOperatorType<T> = typeof startWithOperator & StartWithBrand
109117
export type debounceTimeOperatorType<T> = typeof debounceTimeOperator & DebounceTimeBrand;
110118
export type auditTimeOperatorType<T> = typeof auditTimeOperator & AuditTimeBrand;
111119
export type takeUntilOperatorType<T> = typeof takeUntilOperator & TakeUntilBrand;
120+
export type combineLatestOperatorType<T, R> = typeof combineLatestOperator & CombineLatestBrand;
112121
export type delayOperatorType<T> = typeof delayOperator & DelayBrand;
113122

114-
// We add `Function` to the type intersection to make this nomically different from
123+
// We add `Function` to the type intersection to make this nominally different from
115124
// `finallyOperatorType` while still being structurally the same. Without this, TypeScript tries to
116125
// reduce `typeof _finallyOperator & FinallyBrand` to `finallyOperatorType<T>` and then fails
117126
// because `T` isn't known.
@@ -129,4 +138,6 @@ export const debounceTime =
129138
debounceTimeOperator as typeof debounceTimeOperator & DebounceTimeBrand & Function;
130139
export const auditTime = auditTimeOperator as typeof auditTimeOperator & AuditTimeBrand & Function;
131140
export const takeUntil = takeUntilOperator as typeof takeUntilOperator & TakeUntilBrand & Function;
141+
export const combineLatest =
142+
combineLatestOperator as typeof combineLatestOperator & CombineLatestBrand & Function;
132143
export const delay = delayOperator as typeof delayOperator & DelayBrand & Function;

src/demo-app/table/table-demo.html

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -59,24 +59,24 @@ <h3>CdkTable Example</h3>
5959
</div>
6060

6161
<cdk-table #table matSort
62-
[dataSource]="dataSource"
63-
[trackBy]="userTrackBy">
62+
[dataSource]="dataSource"
63+
[trackBy]="userTrackBy">
6464

6565
<!-- Column Definition: ID -->
6666
<ng-container cdkColumnDef="userId">
6767
<cdk-header-cell *cdkHeaderCellDef
6868
mat-sort-header arrowPosition="before">
69-
ID
70-
</cdk-header-cell>
69+
ID
70+
</cdk-header-cell>
7171
<cdk-cell *cdkCellDef="let row"> {{row.id}} </cdk-cell>
7272
</ng-container>
7373

7474
<!-- Column Definition: Progress -->
7575
<ng-container cdkColumnDef="progress">
7676
<cdk-header-cell *cdkHeaderCellDef
77-
mat-sort-header start="desc">
78-
Progress
79-
</cdk-header-cell>
77+
mat-sort-header start="desc">
78+
Progress
79+
</cdk-header-cell>
8080
<cdk-cell *cdkCellDef="let row">
8181
<div class="demo-progress-stat">{{row.progress}}%</div>
8282
<div class="demo-progress-indicator-container">
@@ -91,30 +91,30 @@ <h3>CdkTable Example</h3>
9191
<!-- Column Definition: Name -->
9292
<ng-container cdkColumnDef="userName">
9393
<cdk-header-cell *cdkHeaderCellDef mat-sort-header>
94-
Name
95-
</cdk-header-cell>
94+
Name
95+
</cdk-header-cell>
9696
<cdk-cell *cdkCellDef="let row"> {{row.name}} </cdk-cell>
9797
</ng-container>
9898

9999
<!-- Column Definition: Color -->
100100
<ng-container cdkColumnDef="color">
101101
<cdk-header-cell *cdkHeaderCellDef
102-
mat-sort-header disableClear>
103-
Color
104-
</cdk-header-cell>
102+
mat-sort-header disableClear>
103+
Color
104+
</cdk-header-cell>
105105
<cdk-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </cdk-cell>
106106
</ng-container>
107107

108108
<cdk-header-row *cdkHeaderRowDef="displayedColumns"></cdk-header-row>
109109
<cdk-row *cdkRowDef="let row; columns: displayedColumns;
110110
let first = first; let last = last; let even = even; let odd = odd"
111-
[ngClass]="{
111+
[ngClass]="{
112112
'demo-row-highlight-first': highlights.has('first') && first,
113113
'demo-row-highlight-last': highlights.has('last') && last,
114114
'demo-row-highlight-even': highlights.has('even') && even,
115115
'demo-row-highlight-odd': highlights.has('odd') && odd
116116
}">
117-
</cdk-row>
117+
</cdk-row>
118118
</cdk-table>
119119
</mat-card>
120120

@@ -168,7 +168,7 @@ <h3>MatTable Example</h3>
168168
<mat-paginator #paginator
169169
[length]="_peopleDatabase.data.length"
170170
[pageSize]="10"
171-
[pageSizeOptions]="[5, 10, 25, 100]">
171+
[pageSizeOptions]="[5, 10, 25, 100]">
172172
</mat-paginator>
173173
</div>
174174

@@ -200,13 +200,71 @@ <h3> MatTable Using 'When' Rows for Interactive Details</h3>
200200

201201
<mat-header-row *matHeaderRowDef="['userId', 'userName']"></mat-header-row>
202202
<mat-row *matRowDef="let row; columns: ['userId', 'userName'];"
203-
matRipple
204-
class="user-row"
205-
[style.borderBottomColor]="expandedPerson == row ? 'transparent' : ''"
206-
(click)="expandedPerson = row; wasExpanded.add(row)"></mat-row>
203+
matRipple
204+
class="user-row"
205+
[style.borderBottomColor]="expandedPerson == row ? 'transparent' : ''"
206+
(click)="expandedPerson = row; wasExpanded.add(row)"></mat-row>
207207
<mat-row *matRowDef="let row; columns: ['details']; when: isDetailRow"
208-
[@detailExpand]="row.data == expandedPerson ? 'expanded' : 'collapsed'"
209-
style="overflow: hidden">
208+
[@detailExpand]="row.data == expandedPerson ? 'expanded' : 'collapsed'"
209+
style="overflow: hidden">
210210
</mat-row>
211211
</mat-table>
212-
</mat-card>
212+
</mat-card>
213+
214+
<h3>MatTable With MatTableDataSource Example</h3>
215+
216+
<mat-form-field>
217+
<input matInput [formControl]="filter">
218+
</mat-form-field>
219+
220+
<div class="demo-table-container demo-mat-table-example mat-elevation-z4">
221+
222+
<table-header-demo (shiftColumns)="displayedColumns.push(displayedColumns.shift())"
223+
(toggleColorColumn)="toggleColorColumn()">
224+
</table-header-demo>
225+
226+
<mat-table [dataSource]="matTableDataSource" [trackBy]="userTrackBy" matSort
227+
#sortForDataSource="matSort">
228+
229+
<!-- Column Definition: ID -->
230+
<ng-container cdkColumnDef="userId">
231+
<mat-header-cell *matHeaderCellDef mat-sort-header> ID </mat-header-cell>
232+
<mat-cell *matCellDef="let row"> {{row.id}} </mat-cell>
233+
</ng-container>
234+
235+
<!-- Column Definition: Progress -->
236+
<ng-container matColumnDef="progress">
237+
<mat-header-cell *matHeaderCellDef mat-sort-header> Progress </mat-header-cell>
238+
<mat-cell *matCellDef="let row">
239+
<div class="demo-progress-stat">{{row.progress}}%</div>
240+
<div class="demo-progress-indicator-container">
241+
<div class="demo-progress-indicator"
242+
[style.background]="row.progress > 50 ? 'green' : 'red'"
243+
[style.opacity]="getOpacity(row.progress)"
244+
[style.width.%]="row.progress"></div>
245+
</div>
246+
</mat-cell>
247+
</ng-container>
248+
249+
<!-- Column Definition: Name -->
250+
<ng-container matColumnDef="userName">
251+
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
252+
<mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
253+
</ng-container>
254+
255+
<!-- Column Definition: Color -->
256+
<ng-container matColumnDef="color">
257+
<mat-header-cell *matHeaderCellDef mat-sort-header>Color</mat-header-cell>
258+
<mat-cell *matCellDef="let row" [style.color]="row.color"> {{row.color}} </mat-cell>
259+
</ng-container>
260+
261+
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
262+
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
263+
264+
</mat-table>
265+
266+
<mat-paginator #paginatorForDataSource
267+
[length]="_peopleDatabase.data.length"
268+
[pageSizeOptions]="[10, 25, 100]">
269+
</mat-paginator>
270+
</div>

src/demo-app/table/table-demo.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import {Component, ViewChild} from '@angular/core';
22
import {PeopleDatabase, UserData} from './people-database';
33
import {PersonDataSource} from './person-data-source';
4-
import {MatPaginator, MatSort} from '@angular/material';
4+
import {MatPaginator, MatSort, MatTableDataSource} from '@angular/material';
55
import {DetailRow, PersonDetailDataSource} from './person-detail-data-source';
66
import {animate, state, style, transition, trigger} from '@angular/animations';
7+
import {FormControl} from '@angular/forms';
78

89
export type UserProperties = 'userId' | 'userName' | 'progress' | 'color' | undefined;
910

@@ -27,24 +28,48 @@ const properties = ['id', 'name', 'progress', 'color'];
2728
export class TableDemo {
2829
dataSource: PersonDataSource | null;
2930
dataSourceWithDetails: PersonDetailDataSource | null;
31+
matTableDataSource = new MatTableDataSource<UserData>();
3032
displayedColumns: UserProperties[] = [];
3133
trackByStrategy: TrackByStrategy = 'reference';
3234
changeReferences = false;
3335
highlights = new Set<string>();
3436
wasExpanded = new Set<UserData>();
3537

38+
filter = new FormControl();
39+
3640
dynamicColumnDefs: any[] = [];
3741
dynamicColumnIds: string[] = [];
3842

3943
expandedPerson: UserData;
4044

41-
@ViewChild(MatPaginator) _paginator: MatPaginator;
42-
45+
@ViewChild(MatPaginator) paginator: MatPaginator;
4346
@ViewChild(MatSort) sort: MatSort;
4447

4548
isDetailRow = (row: DetailRow|UserData) => row.hasOwnProperty('detailRow');
4649

47-
constructor(public _peopleDatabase: PeopleDatabase) { }
50+
@ViewChild('paginatorForDataSource') paginatorForDataSource: MatPaginator;
51+
@ViewChild('sortForDataSource') sortForDataSource: MatSort;
52+
53+
constructor(public _peopleDatabase: PeopleDatabase) {
54+
this.matTableDataSource.sortingDataAccessor = (data: UserData, property: string) => {
55+
switch (property) {
56+
case 'userId': return +data.id;
57+
case 'userName': return data.name;
58+
case 'progress': return +data.progress;
59+
case 'color': return data.color;
60+
default: return '';
61+
}
62+
};
63+
this.matTableDataSource.filterTermAccessor = (data: UserData) => data.name;
64+
this.filter.valueChanges.subscribe(filter => this.matTableDataSource!.filter = filter);
65+
}
66+
67+
ngAfterViewInit() {
68+
// Needs to be set up after the view is initialized since the data source will look at the sort
69+
// and paginator's initial values to know what data should be rendered.
70+
this.matTableDataSource!.paginator = this.paginatorForDataSource;
71+
this.matTableDataSource!.sort = this.sortForDataSource;
72+
}
4873

4974
ngOnInit() {
5075
this.connect();
@@ -69,9 +94,10 @@ export class TableDemo {
6994
connect() {
7095
this.displayedColumns = ['userId', 'userName', 'progress', 'color'];
7196
this.dataSource = new PersonDataSource(this._peopleDatabase,
72-
this._paginator, this.sort);
97+
this.paginator, this.sort);
7398
this.dataSourceWithDetails = new PersonDetailDataSource(this.dataSource);
7499
this._peopleDatabase.initialize();
100+
this.matTableDataSource!.data = this._peopleDatabase.data.slice();
75101
}
76102

77103
disconnect() {

src/lib/sort/sort-header.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
</div>
1717

1818
<span class="cdk-visually-hidden" *ngIf="_isSorted()">
19-
{{_intl.sortDescriptionLabel(id, _sort.direction)}}
19+
&nbsp;{{_intl.sortDescriptionLabel(id, _sort.direction)}}
2020
</span>

src/lib/sort/sort.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface Sort {
2929
/** Container for MatSortables to manage the sort state and provide default sort parameters. */
3030
@Directive({
3131
selector: '[matSort]',
32-
exportAs: 'matSort',
32+
exportAs: 'matSort'
3333
})
3434
export class MatSort {
3535
/** Collection of all registered sortables that this directive manages. */

src/lib/table/public-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ export * from './table-module';
1010
export * from './cell';
1111
export * from './table';
1212
export * from './row';
13-
13+
export * from './table-data-source';

0 commit comments

Comments
 (0)