Skip to content

Commit ddb411d

Browse files
committed
add overview
1 parent ecf9e98 commit ddb411d

File tree

5 files changed

+341
-8
lines changed

5 files changed

+341
-8
lines changed

src/cdk/table/table.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ and Column A and in that order, then the template would look like this:
7878
```
7979

8080
Adding attribute and event binding to the header and rows is as simple as applying them to the
81-
<cdk-header-row> and <cdk-row>. For example, here the table is adding a click handler to both.
82-
In addition, the CSS class `a-bigger-than-twenty` will be applied to any row where its data’s `a` property is greater than 20.
81+
`<cdk-header-row>` and `<cdk-row>`. For example, here the table is adding a click handler to both.
82+
In addition, the CSS class `a-bigger-than-twenty` will be applied to any row where its data’s `a`
83+
property is greater than 20.
8384

8485
```html
8586
<cdk-header-row *cdkHeaderRowDef="['column_b', 'column_a']"

src/material-examples/example-module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ import {
8383
MdChipsModule, MdDatepickerModule, MdDialogModule, MdGridListModule, MdIconModule, MdInputModule,
8484
MdListModule, MdMenuModule, MdPaginatorModule, MdProgressBarModule, MdProgressSpinnerModule,
8585
MdRadioModule, MdSelectModule, MdSidenavModule, MdSliderModule, MdSlideToggleModule,
86-
MdSnackBarModule, MdTabsModule, MdToolbarModule, MdTooltipModule
86+
MdSnackBarModule, MdSortModule, MdTabsModule, MdToolbarModule, MdTooltipModule
8787
} from '@angular/material';
8888
import {CdkTableModule} from '@angular/cdk';
8989
import {TableOverviewExample} from './table-overview/table-overview-example';
@@ -224,6 +224,7 @@ export const EXAMPLE_COMPONENTS = {
224224
MdProgressBarModule,
225225
MdProgressSpinnerModule,
226226
MdRadioModule,
227+
MdSortModule,
227228
MdSelectModule,
228229
MdSlideToggleModule,
229230
MdSliderModule,
Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,84 @@
1-
/** No CSS for this example */
1+
/* Structure */
2+
.example-container {
3+
display: flex;
4+
flex-direction: column;
5+
max-height: 500px;
6+
background: white;
7+
min-width: 300px;
8+
}
9+
10+
.example-header {
11+
min-height: 56px;
12+
display: flex;
13+
align-items: center;
14+
padding: 8px 24px 0 24px;
15+
font-size: 20px;
16+
justify-content: space-between;
17+
border-bottom: 1px solid transparent;
18+
}
19+
20+
.mat-input-container {
21+
font-size: 14px;
22+
flex-grow: 1;
23+
margin-left: 32px;
24+
margin-top: 8px;
25+
}
26+
27+
.example-no-results {
28+
display: flex;
29+
justify-content: center;
30+
padding: 24px;
31+
font-size: 12px;
32+
font-style: italic;
33+
}
34+
35+
/** Selection styles */
36+
.example-selection-header {
37+
font-size: 18px;
38+
background: rgba(255, 64, 129, .3);
39+
border-bottom: 1px solid #d696ac;
40+
}
41+
42+
.cdk-column-select {
43+
max-width: 54px;
44+
}
45+
46+
.cdk-row:hover, .example-selected-row {
47+
background: #F5F5F5;
48+
}
49+
50+
.cdk-row:active, .cdk-row.example-selected-row {
51+
background: #EAEAEA;
52+
}
53+
54+
/*
55+
* Styles to make the demo's cdk-table match the material design spec
56+
* https://material.io/guidelines/components/data-tables.html
57+
*/
58+
.cdk-table {
59+
flex: 1 1 auto;
60+
overflow: auto;
61+
}
62+
63+
.cdk-row, .cdk-header-row {
64+
display: flex;
65+
border-bottom: 1px solid #ccc;
66+
align-items: center;
67+
height: 48px;
68+
padding: 0 24px;
69+
}
70+
71+
.cdk-cell, .cdk-header-cell {
72+
flex: 1;
73+
}
74+
75+
.cdk-header-cell {
76+
font-size: 12px;
77+
font-weight: bold;
78+
color: rgba(0, 0, 0, 0.54);
79+
}
80+
81+
.cdk-cell {
82+
font-size: 13px;
83+
color: rgba(0, 0, 0, 0.87);
84+
}
Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,79 @@
1-
Table example coming soon
1+
<div class="example-container mat-elevation-z8">
2+
<div class="example-header" [style.display]="selection.isEmpty() ? '' : 'none'">
3+
Users
4+
5+
<md-input-container floatPlaceholder="never">
6+
<input mdInput #filter placeholder="Filter users">
7+
</md-input-container>
8+
</div>
9+
<div class="example-header example-selection-header"
10+
*ngIf="!selection.isEmpty()">
11+
{{selection.selected.length}}
12+
{{selection.selected.length == 1 ? 'user' : 'users'}}
13+
selected
14+
</div>
15+
16+
<cdk-table #table [dataSource]="dataSource" mdSort>
17+
18+
<!--- Note that these columns can be defined in any order.
19+
The actual rendered columns are set as a property on the row definition" -->
20+
21+
<!-- Checkbox Column -->
22+
<ng-container cdkColumnDef="select">
23+
<cdk-header-cell *cdkHeaderCellDef>
24+
<md-checkbox (change)="$event ? masterToggle() : null"
25+
[checked]="isAllSelected()"
26+
[indeterminate]="selection.hasValue() && !isAllSelected()">
27+
</md-checkbox>
28+
</cdk-header-cell>
29+
<cdk-cell *cdkCellDef="let row">
30+
<md-checkbox (click)="$event.stopPropagation()"
31+
(change)="$event ? selection.toggle(row.id) : null"
32+
[checked]="selection.isSelected(row.id)">
33+
</md-checkbox>
34+
</cdk-cell>
35+
</ng-container>
36+
37+
<!-- ID Column -->
38+
<ng-container cdkColumnDef="userId">
39+
<cdk-header-cell *cdkHeaderCellDef md-sort-header> ID </cdk-header-cell>
40+
<cdk-cell *cdkCellDef="let row"> {{row.id}} </cdk-cell>
41+
</ng-container>
42+
43+
<!-- Progress Column -->
44+
<ng-container cdkColumnDef="progress">
45+
<cdk-header-cell *cdkHeaderCellDef md-sort-header> Progress </cdk-header-cell>
46+
<cdk-cell *cdkCellDef="let row"> {{row.progress}}% </cdk-cell>
47+
</ng-container>
48+
49+
<!-- Name Column -->
50+
<ng-container cdkColumnDef="userName">
51+
<cdk-header-cell *cdkHeaderCellDef md-sort-header> Name </cdk-header-cell>
52+
<cdk-cell *cdkCellDef="let row"> {{row.name}} </cdk-cell>
53+
</ng-container>
54+
55+
<!-- Color Column -->
56+
<ng-container cdkColumnDef="color">
57+
<cdk-header-cell *cdkHeaderCellDef md-sort-header> Color </cdk-header-cell>
58+
<cdk-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </cdk-cell>
59+
</ng-container>
60+
61+
<cdk-header-row *cdkHeaderRowDef="displayedColumns"></cdk-header-row>
62+
<cdk-row *cdkRowDef="let row; columns: displayedColumns;"
63+
[class.example-selected-row]="selection.isSelected(row.id)"
64+
(click)="selection.toggle(row.id)">
65+
</cdk-row>
66+
</cdk-table>
67+
68+
<div class="example-no-results"
69+
[style.display]="dataSource.renderedData.length == 0 ? '' : 'none'">
70+
No users found matching filter.
71+
</div>
72+
73+
<md-paginator #paginator
74+
[length]="dataSource.renderedData.length"
75+
[pageIndex]="0"
76+
[pageSize]="25"
77+
[pageSizeOptions]="[5, 10, 25, 100]">
78+
</md-paginator>
79+
</div>
Lines changed: 173 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,180 @@
1-
import {Component} from '@angular/core';
2-
1+
import {Component, ElementRef, ViewChild} from '@angular/core';
2+
import {DataSource} from '@angular/cdk';
3+
import {MdPaginator, MdSort, SelectionModel} from '@angular/material';
4+
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
5+
import {Observable} from 'rxjs/Observable';
6+
import 'rxjs/add/operator/startWith';
7+
import 'rxjs/add/observable/merge';
8+
import 'rxjs/add/observable/fromEvent';
9+
import 'rxjs/add/operator/map';
10+
import 'rxjs/add/operator/distinctUntilChanged';
11+
import 'rxjs/add/operator/debounceTime';
312

413
@Component({
514
selector: 'table-overview-example',
6-
templateUrl: 'table-overview-example.html',
715
styleUrls: ['table-overview-example.css'],
16+
templateUrl: 'table-overview-example.html',
817
})
918
export class TableOverviewExample {
19+
displayedColumns = ['select', 'userId', 'userName', 'progress', 'color'];
20+
exampleDatabase = new ExampleDatabase();
21+
selection = new SelectionModel<string>(true, []);
22+
dataSource: ExampleDataSource | null;
23+
24+
@ViewChild(MdPaginator) paginator: MdPaginator;
25+
@ViewChild(MdSort) sort: MdSort;
26+
@ViewChild('filter') filter: ElementRef;
27+
28+
ngOnInit() {
29+
this.dataSource = new ExampleDataSource(this.exampleDatabase, this.paginator, this.sort);
30+
Observable.fromEvent(this.filter.nativeElement, 'keyup')
31+
.debounceTime(150)
32+
.distinctUntilChanged()
33+
.subscribe(() => {
34+
if (!this.dataSource) { return; }
35+
this.dataSource.filter = this.filter.nativeElement.value;
36+
});
37+
}
38+
39+
isAllSelected(): boolean {
40+
if (!this.dataSource) { return false; }
41+
if (this.selection.isEmpty()) { return false; }
42+
43+
if (this.filter.nativeElement.value) {
44+
return this.selection.selected.length == this.dataSource.renderedData.length;
45+
} else {
46+
return this.selection.selected.length == this.exampleDatabase.data.length;
47+
}
48+
}
49+
50+
masterToggle() {
51+
if (!this.dataSource) { return; }
52+
53+
if (this.isAllSelected()) {
54+
this.selection.clear();
55+
} else if (this.filter.nativeElement.value) {
56+
this.dataSource.renderedData.forEach(data => this.selection.select(data.id));
57+
} else {
58+
this.exampleDatabase.data.forEach(data => this.selection.select(data.id));
59+
}
60+
}
61+
}
62+
63+
/** Constants used to fill up our data base. */
64+
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
65+
'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
66+
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
67+
'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
68+
'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
69+
70+
export interface UserData {
71+
id: string;
72+
name: string;
73+
progress: string;
74+
color: string;
75+
}
76+
77+
/** An example database that the data source uses to retrieve data for the table. */
78+
export class ExampleDatabase {
79+
/** Stream that emits whenever the data has been modified. */
80+
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
81+
get data(): UserData[] { return this.dataChange.value; }
82+
83+
constructor() {
84+
// Fill up the database with 100 users.
85+
for (let i = 0; i < 100; i++) { this.addUser(); }
86+
}
87+
88+
/** Adds a new user to the database. */
89+
addUser() {
90+
const copiedData = this.data.slice();
91+
copiedData.push(this.createNewUser());
92+
this.dataChange.next(copiedData);
93+
}
94+
95+
/** Builds and returns a new User. */
96+
private createNewUser() {
97+
const name =
98+
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
99+
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
100+
101+
return {
102+
id: (this.data.length + 1).toString(),
103+
name: name,
104+
progress: Math.round(Math.random() * 100).toString(),
105+
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
106+
};
107+
}
108+
}
109+
110+
/**
111+
* Data source to provide what data should be rendered in the table. Note that the data source
112+
* can retrieve its data in any way. In this case, the data source is provided a reference
113+
* to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
114+
* the underlying data. Instead, it only needs to take the data and send the table exactly what
115+
* should be rendered.
116+
*/
117+
export class ExampleDataSource extends DataSource<any> {
118+
_filterChange = new BehaviorSubject('');
119+
get filter(): string { return this._filterChange.value; }
120+
set filter(filter: string) { this._filterChange.next(filter); }
121+
122+
renderedData: UserData[] = [];
123+
124+
constructor(private _exampleDatabase: ExampleDatabase,
125+
private _paginator: MdPaginator,
126+
private _sort: MdSort) {
127+
super();
128+
}
129+
130+
/** Connect function called by the table to retrieve one stream containing the data to render. */
131+
connect(): Observable<UserData[]> {
132+
// Listen for any changes in the base data, sorting, filtering, or pagination
133+
const displayDataChanges = [
134+
this._exampleDatabase.dataChange,
135+
this._sort.mdSortChange,
136+
this._filterChange,
137+
this._paginator.page,
138+
];
139+
140+
return Observable.merge(...displayDataChanges).map(() => {
141+
// Filter data
142+
const filteredData = this._exampleDatabase.data.slice().filter((item: UserData) => {
143+
let searchStr = (item.name + item.color).toLowerCase();
144+
return searchStr.indexOf(this.filter.toLowerCase()) != -1;
145+
});
146+
147+
// Sort filtered data
148+
const sortedData = this.sortData(filteredData);
149+
150+
// Grab the page's slice of the filtered sorted data.
151+
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
152+
this.renderedData = sortedData.splice(startIndex, this._paginator.pageSize);
153+
return this.renderedData;
154+
});
155+
}
156+
157+
disconnect() {}
158+
159+
/** Returns a sorted copy of the database data. */
160+
sortData(data: UserData[]): UserData[] {
161+
if (!this._sort.active || this._sort.direction == '') { return data; }
162+
163+
return data.sort((a, b) => {
164+
let propertyA: number|string = '';
165+
let propertyB: number|string = '';
166+
167+
switch (this._sort.active) {
168+
case 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
169+
case 'userName': [propertyA, propertyB] = [a.name, b.name]; break;
170+
case 'progress': [propertyA, propertyB] = [a.progress, b.progress]; break;
171+
case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
172+
}
173+
174+
let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
175+
let valueB = isNaN(+propertyB) ? propertyB : +propertyB;
176+
177+
return (valueA < valueB ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1);
178+
});
179+
}
10180
}

0 commit comments

Comments
 (0)