Table docs (#5449)

* docs(table): initial table docs

* add filtering msg

* add overview

* fix height

* split cdk table into guide

* revisions

* add to examples

* fix heading

* example changes

* lint

* example style prefix, rename file

* newline
This commit is contained in:
Andrew Seguin
2017-07-05 16:28:51 -07:00
committed by mmalerba
parent 05302b8380
commit 6499ccbb5c
26 changed files with 1382 additions and 15 deletions
+128
View File
@@ -0,0 +1,128 @@
The `<cdk-table>` is an unopinionated, customizable data-table with a fully-templated API, dynamic
columns, and an accessible DOM structure. This component acts as the core upon which anyone can
build their own tailored data-table experience.
The table provides a foundation upon which other features, such as sorting and pagination, can be
built. Because it enforces no opinions on these matters, developers have full control over the
interaction patterns associated with the table.
For a Material Design styled table, see the documentation for `<md-table>` which builds on top of
the CDK data-table.
<!-- example(cdk-table-basic) -->
### Using the CDK data-table
#### Writing your table template
The first step to writing the data-table template is to define the columns.
A column definition is specified via an `<ng-container>` with the `cdkColumnDef` directive, giving
column a name. Each column definition then further defines both a header-cell template
(`cdkHeaderCellDef`) and a data-cell template (`cdkCellDef`).
```html
<ng-container cdkColumnDef="username">
<cdk-header-cell *cdkHeaderCellDef> User name </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.a}} </cdk-cell>
</ng-container>
```
The set of columns defined represent the columns that are _available_ to be rendered. The specific
columns rendered in a given row, and their order, are specified on the row (see below).
Note that `cdkCellDef` exports the row context such that the row data can be referenced in the cell
template. The directive also exports the same properties as `ngFor` (index, even, odd, first,
last).
The next step is to define the table's header-row (`cdkHeaderRowDef`) and data-row (`cdkRowDef`).
```html
<cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></cdk-header-row>
<cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></cdk-row>
```
These row templates accept the specific columns to be rendered via the name given to the
`cdkColumnDef`.
The `cdkRowDef` also exports row context, which can be used for event and property
bindings on the row element. Any content placed _inside_ of the header row or data row template
will be ignored, as the rendered content of the row comes from the cell templates described
above.
##### Example: table with three columns
```html
<cdk-table [dataSource]="dataSource">
<!-- User name Definition -->
<ng-container cdkColumnDef="username">
<cdk-header-cell *cdkHeaderCellDef> User name </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.username}} </cdk-cell>
</ng-container>
<!-- Age Definition -->
<ng-container cdkColumnDef="age">
<cdk-header-cell *cdkHeaderCellDef> Age </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.age}} </cdk-cell>
</ng-container>
<!-- Title Definition -->
<ng-container cdkColumnDef="title">
<cdk-header-cell *cdkHeaderCellDef> Title </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.title}} </cdk-cell>
</ng-container>
<!-- Header and Row Declarations -->
<cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></cdk-header-row>
<cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></cdk-row>
</cdk-table>
```
The columns given on the row determine which cells are rendered and in which order. Thus, the
columns can be set via binding to support dynamically changing the columns shown at run-time.
It is not required to display all the columns that are defined within the template,
nor use the same ordering. For example, to display the table with only `age`
and `username` and in that order, then the row and header definitions would be written as:
```html
<cdk-row *cdkRowDef="let row; columns: myDisplayedColumns"></cdk-row>
```
Event and property bindings can be added directly to the row element.
##### Example: table with event and class binding
```html
<cdk-header-row *cdkHeaderRowDef="['age', 'username']"
(click)=”handleHeaderRowClick(row)”>
</cdk-header-row>
<cdk-row *cdkRowDef="let row; columns: ['age', 'username']"
[class.can-vote]=”row.age >= 18”
(click)=”handleRowClick(row)”>
</cdk-row>
```
#### Connecting the table to a data source
Data is provided to the table through a `DataSource`. When the table receives a data source,
it calls the DataSource's connect function which returns an observable that emits an array of data.
Whenever the data source emits data to this stream, the table will update.
Because the _data source_ provides this stream, it bears the responsibility of triggering table
updates. This can be based on _anything_: websocket connections, user interaction, model updates,
time-based intervals, etc. Most commonly, updates will be triggered by user interactions like
sorting and pagination.
##### `trackBy`
To improve performance, a trackBy function can be provided to the table similar to Angular’s
(`ngFor` trackBy)[trackBy]. This informs the table how to uniquely identify rows to track how the
data changes with each update.
```html
<cdk-table [dataSource]="dataSource" [trackBy]="myTrackById">
```
[trackBy][https://angular.io/api/common/NgForOf#change-propagation]
+5 -5
View File
@@ -76,8 +76,8 @@
<cdk-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </cdk-cell>
</ng-container>
<cdk-header-row *cdkHeaderRowDef="propertiesToDisplay"></cdk-header-row>
<cdk-row *cdkRowDef="let row; columns: propertiesToDisplay;
<cdk-header-row *cdkHeaderRowDef="displayedColumns"></cdk-header-row>
<cdk-row *cdkRowDef="let row; columns: displayedColumns;
let first = first; let last = last; let even = even; let odd = odd"
[ngClass]="{
'demo-row-highlight-first': highlights.has('first') && first,
@@ -92,7 +92,7 @@
<div class="demo-table-container demo-mat-table-example mat-elevation-z4">
<table-header-demo (shiftColumns)="propertiesToDisplay.push(propertiesToDisplay.shift())"
<table-header-demo (shiftColumns)="displayedColumns.push(displayedColumns.shift())"
(toggleColorColumn)="toggleColorColumn()">
</table-header-demo>
@@ -130,8 +130,8 @@
<md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="propertiesToDisplay"></md-header-row>
<md-row *cdkRowDef="let row; columns: propertiesToDisplay"></md-row>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns"></md-row>
</md-table>
+6 -6
View File
@@ -16,7 +16,7 @@ export type TrackByStrategy = 'id' | 'reference' | 'index';
})
export class TableDemo {
dataSource: PersonDataSource | null;
propertiesToDisplay: UserProperties[] = [];
displayedColumns: UserProperties[] = [];
trackByStrategy: TrackByStrategy = 'reference';
changeReferences = false;
highlights = new Set<string>();
@@ -32,7 +32,7 @@ export class TableDemo {
}
connect() {
this.propertiesToDisplay = ['userId', 'userName', 'progress', 'color'];
this.displayedColumns = ['userId', 'userName', 'progress', 'color'];
this.dataSource = new PersonDataSource(this._peopleDatabase,
this._paginator, this.sort);
this._peopleDatabase.initialize();
@@ -40,7 +40,7 @@ export class TableDemo {
disconnect() {
this.dataSource = null;
this.propertiesToDisplay = [];
this.displayedColumns = [];
}
getOpacity(progress: number) {
@@ -57,11 +57,11 @@ export class TableDemo {
}
toggleColorColumn() {
let colorColumnIndex = this.propertiesToDisplay.indexOf('color');
let colorColumnIndex = this.displayedColumns.indexOf('color');
if (colorColumnIndex == -1) {
this.propertiesToDisplay.push('color');
this.displayedColumns.push('color');
} else {
this.propertiesToDisplay.splice(colorColumnIndex, 1);
this.displayedColumns.splice(colorColumnIndex, 1);
}
}
+49
View File
@@ -0,0 +1,49 @@
The `md-table` provides a Material Design styled data-table that can be used to display rows of
data.
This table builds on the foundation of the CDK data-table and uses a similar interface for its
data source input and template, except that its element selectors will be prefixed with `md-`
instead of `cdk-`.
<!-- example(table-basic) -->
Note that the column definition directives (`cdkColumnDef` and `cdkHeaderCellDef`) are still
prefixed with `cdk-`.
For more information on the interface and how it works, see the guide covering the CDK data-table.
### Features
The `<md-table>` itself only deals with the rendering of a table structure (rows and cells).
Additional features can be built on top of the table by adding behavior inside cell templates
(e.g., sort headers) or next to the table (e.g. a paginator). Interactions that affect the
rendered data (such as sorting and pagination) should be propagated through the table's data source.
#### Pagination
The `<md-paginator>` adds a pagination UI that can be used in conjunction with the `<md-table>`. The
paginator emits events that can be used to trigger an update via the table's data source.
<!-- example(table-pagination) -->
#### Sorting
Use the `mdSort` directive and `<md-sort-header>` adds a sorting UI the table's column headers. The
sort headers emit events that can be used to trigger an update via the table's data source.
<!-- example(table-sorting) -->
#### Filtering
While Angular Material does not offer a specific component for filtering tabular data, the table's
data source can be updated based on any custom filter UI. Any filtering pattern need only trigger
an update via the table's data source.
<!--- example(table-filtering) -->
### Simple Table
In the near future, we will provide a simplified version of the data-table with an easy-to-use
interface, material styling, array input, and more out-of-the-box features (sorting, pagination,
and selection).
@@ -0,0 +1,39 @@
/* Structure */
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
min-width: 300px;
}
/*
* Styles to make the demo's cdk-table match the material design spec
* https://material.io/guidelines/components/data-tables.html
*/
.example-table {
flex: 1 1 auto;
overflow: auto;
}
.example-header-row, .example-row {
display: flex;
border-bottom: 1px solid #ccc;
align-items: center;
height: 32px;
padding: 0 8px;
}
.example-cell, .example-header-cell {
flex: 1;
}
.example-header-cell {
font-size: 12px;
font-weight: bold;
color: rgba(0, 0, 0, 0.54);
}
.example-cell {
font-size: 13px;
color: rgba(0, 0, 0, 0.87);
}
@@ -0,0 +1,36 @@
<div class="example-container mat-elevation-z8">
<cdk-table #table [dataSource]="dataSource" class="example-table">
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- ID Column -->
<ng-container cdkColumnDef="userId">
<cdk-header-cell *cdkHeaderCellDef class="example-header-cell"> ID </cdk-header-cell>
<cdk-cell *cdkCellDef="let row" class="example-cell"> {{row.id}} </cdk-cell>
</ng-container>
<!-- Progress Column -->
<ng-container cdkColumnDef="progress">
<cdk-header-cell *cdkHeaderCellDef class="example-header-cell"> Progress </cdk-header-cell>
<cdk-cell *cdkCellDef="let row" class="example-cell"> {{row.progress}}% </cdk-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="userName">
<cdk-header-cell *cdkHeaderCellDef class="example-header-cell"> Name </cdk-header-cell>
<cdk-cell *cdkCellDef="let row" class="example-cell"> {{row.name}} </cdk-cell>
</ng-container>
<!-- Color Column -->
<ng-container cdkColumnDef="color">
<cdk-header-cell *cdkHeaderCellDef class="example-header-cell">Color</cdk-header-cell>
<cdk-cell *cdkCellDef="let row" class="example-cell"
[style.color]="row.color">
{{row.color}}
</cdk-cell>
</ng-container>
<cdk-header-row *cdkHeaderRowDef="displayedColumns" class="example-header-row"></cdk-header-row>
<cdk-row *cdkRowDef="let row; columns: displayedColumns;" class="example-row"></cdk-row>
</cdk-table>
</div>
@@ -0,0 +1,89 @@
import {Component} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
@Component({
selector: 'cdk-table-basic-example',
styleUrls: ['cdk-table-basic-example.css'],
templateUrl: 'cdk-table-basic-example.html',
})
export class CdkTableBasicExample {
displayedColumns = ['userId', 'userName', 'progress', 'color'];
exampleDatabase = new ExampleDatabase();
dataSource: ExampleDataSource | null;
ngOnInit() {
this.dataSource = new ExampleDataSource(this.exampleDatabase);
}
}
/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
export interface UserData {
id: string;
name: string;
progress: string;
color: string;
}
/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
/** Stream that emits whenever the data has been modified. */
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
get data(): UserData[] { return this.dataChange.value; }
constructor() {
// Fill up the database with 100 users.
for (let i = 0; i < 100; i++) { this.addUser(); }
}
/** Adds a new user to the database. */
addUser() {
const copiedData = this.data.slice();
copiedData.push(this.createNewUser());
this.dataChange.next(copiedData);
}
/** Builds and returns a new User. */
private createNewUser() {
const name =
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
return {
id: (this.data.length + 1).toString(),
name: name,
progress: Math.round(Math.random() * 100).toString(),
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
};
}
}
/**
* Data source to provide what data should be rendered in the table. Note that the data source
* can retrieve its data in any way. In this case, the data source is provided a reference
* to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
* the underlying data. Instead, it only needs to take the data and send the table exactly what
* should be rendered.
*/
export class ExampleDataSource extends DataSource<any> {
constructor(private _exampleDatabase: ExampleDatabase) {
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<UserData[]> {
return this._exampleDatabase.dataChange;
}
disconnect() {}
}
+23 -1
View File
@@ -89,8 +89,15 @@ import {
MdChipsModule, MdDatepickerModule, MdDialogModule, MdGridListModule, MdIconModule, MdInputModule,
MdListModule, MdMenuModule, MdPaginatorModule, MdProgressBarModule, MdProgressSpinnerModule,
MdRadioModule, MdSelectModule, MdSidenavModule, MdSliderModule, MdSlideToggleModule,
MdSnackBarModule, MdTabsModule, MdToolbarModule, MdTooltipModule
MdSnackBarModule, MdSortModule, MdTableModule, MdTabsModule, MdToolbarModule, MdTooltipModule
} from '@angular/material';
import {CdkTableModule} from '@angular/cdk';
import {TableOverviewExample} from './table-overview/table-overview-example';
import {TablePaginationExample} from './table-pagination/table-pagination-example';
import {TableBasicExample} from './table-basic/table-basic-example';
import {TableSortingExample} from './table-sorting/table-sorting-example';
import {TableFilteringExample} from './table-filtering/table-filtering-example';
import {CdkTableBasicExample} from './cdk-table-basic/cdk-table-basic-example';
export interface LiveExample {
title: string;
@@ -114,6 +121,7 @@ export const EXAMPLE_COMPONENTS = {
},
'button-toggle-overview': {title: 'Basic button-toggles', component: ButtonToggleOverviewExample},
'chips-overview': {title: 'Basic chips', component: ChipsOverviewExample},
'cdk-table-basic': {title: 'Basic CDK data-table', component: CdkTableBasicExample},
'chips-stacked': {title: 'Stacked chips', component: ChipsStackedExample},
'card-fancy': {title: 'Card with multiple sections', component: CardFancyExample},
'card-overview': {title: 'Basic cards', component: CardOverviewExample},
@@ -194,6 +202,11 @@ export const EXAMPLE_COMPONENTS = {
component: SnackBarComponentExample
},
'snack-bar-overview': {title: 'Basic snack-bar', component: SnackBarOverviewExample},
'table-overview': {title: 'Feature-rich data table', component: TableOverviewExample},
'table-pagination': {title: 'Table with pagination', component: TablePaginationExample},
'table-sorting': {title: 'Table with sorting', component: TableSortingExample},
'table-filtering': {title: 'Table with filtering', component: TableFilteringExample},
'table-basic': {title: 'Basic table', component: TableBasicExample},
'tabs-overview': {title: 'Basic tabs', component: TabsOverviewExample},
'tabs-template-label': {title: 'Coming soon!', component: TabsTemplateLabelExample},
'toolbar-multirow': {title: 'Multi-row toolbar', component: ToolbarMultirowExample},
@@ -207,6 +220,7 @@ export const EXAMPLE_COMPONENTS = {
*/
@NgModule({
exports: [
CdkTableModule,
MdAutocompleteModule,
MdButtonModule,
MdButtonToggleModule,
@@ -224,11 +238,13 @@ export const EXAMPLE_COMPONENTS = {
MdProgressBarModule,
MdProgressSpinnerModule,
MdRadioModule,
MdSortModule,
MdSelectModule,
MdSlideToggleModule,
MdSliderModule,
MdSidenavModule,
MdSnackBarModule,
MdTableModule,
MdTabsModule,
MdToolbarModule,
MdTooltipModule
@@ -248,6 +264,7 @@ export const EXAMPLE_LIST = [
ButtonTypesExample,
CardFancyExample,
CardOverviewExample,
CdkTableBasicExample,
ChipsOverviewExample,
ChipsStackedExample,
CheckboxConfigurableExample,
@@ -298,6 +315,11 @@ export const EXAMPLE_LIST = [
SnackBarComponentExample,
PizzaPartyComponent,
SnackBarOverviewExample,
TableBasicExample,
TableOverviewExample,
TableFilteringExample,
TablePaginationExample,
TableSortingExample,
TabsOverviewExample,
TabsTemplateLabelExample,
ToolbarMultirowExample,
@@ -0,0 +1,20 @@
/* Structure */
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
background: white;
min-width: 300px;
}
.example-header {
min-height: 64px;
display: flex;
align-items: center;
padding-left: 24px;
font-size: 20px;
}
.mat-table {
overflow: auto;
}
@@ -0,0 +1,36 @@
<div class="example-container mat-elevation-z8">
<div class="example-header"> Users </div>
<md-table #table [dataSource]="dataSource">
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- ID Column -->
<ng-container cdkColumnDef="userId">
<md-header-cell *cdkHeaderCellDef> ID </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
</ng-container>
<!-- Progress Column -->
<ng-container cdkColumnDef="progress">
<md-header-cell *cdkHeaderCellDef> Progress </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.progress}}% </md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="userName">
<md-header-cell *cdkHeaderCellDef> Name </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
</ng-container>
<!-- Color Column -->
<ng-container cdkColumnDef="color">
<md-header-cell *cdkHeaderCellDef>Color</md-header-cell>
<md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
</md-table>
</div>
@@ -0,0 +1,89 @@
import {Component} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
@Component({
selector: 'table-basic-example',
styleUrls: ['table-basic-example.css'],
templateUrl: 'table-basic-example.html',
})
export class TableBasicExample {
displayedColumns = ['userId', 'userName', 'progress', 'color'];
exampleDatabase = new ExampleDatabase();
dataSource: ExampleDataSource | null;
ngOnInit() {
this.dataSource = new ExampleDataSource(this.exampleDatabase);
}
}
/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
export interface UserData {
id: string;
name: string;
progress: string;
color: string;
}
/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
/** Stream that emits whenever the data has been modified. */
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
get data(): UserData[] { return this.dataChange.value; }
constructor() {
// Fill up the database with 100 users.
for (let i = 0; i < 100; i++) { this.addUser(); }
}
/** Adds a new user to the database. */
addUser() {
const copiedData = this.data.slice();
copiedData.push(this.createNewUser());
this.dataChange.next(copiedData);
}
/** Builds and returns a new User. */
private createNewUser() {
const name =
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
return {
id: (this.data.length + 1).toString(),
name: name,
progress: Math.round(Math.random() * 100).toString(),
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
};
}
}
/**
* Data source to provide what data should be rendered in the table. Note that the data source
* can retrieve its data in any way. In this case, the data source is provided a reference
* to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
* the underlying data. Instead, it only needs to take the data and send the table exactly what
* should be rendered.
*/
export class ExampleDataSource extends DataSource<any> {
constructor(private _exampleDatabase: ExampleDatabase) {
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<UserData[]> {
return this._exampleDatabase.dataChange;
}
disconnect() {}
}
@@ -0,0 +1,35 @@
/* Structure */
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
background: white;
min-width: 300px;
}
.example-header {
min-height: 64px;
display: flex;
align-items: center;
padding-left: 24px;
font-size: 20px;
}
.example-header {
min-height: 64px;
display: flex;
align-items: baseline;
padding: 8px 24px 0;
font-size: 20px;
justify-content: space-between;
}
.mat-input-container {
font-size: 14px;
flex-grow: 1;
margin-left: 32px;
}
.mat-table {
overflow: auto;
}
@@ -0,0 +1,42 @@
<div class="example-container mat-elevation-z8">
<div class="example-header">
Users
<md-input-container floatPlaceholder="never">
<input mdInput #filter placeholder="Filter users">
</md-input-container>
</div>
<md-table #table [dataSource]="dataSource">
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- ID Column -->
<ng-container cdkColumnDef="userId">
<md-header-cell *cdkHeaderCellDef> ID </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
</ng-container>
<!-- Progress Column -->
<ng-container cdkColumnDef="progress">
<md-header-cell *cdkHeaderCellDef> Progress </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.progress}}% </md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="userName">
<md-header-cell *cdkHeaderCellDef> Name </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
</ng-container>
<!-- Color Column -->
<ng-container cdkColumnDef="color">
<md-header-cell *cdkHeaderCellDef> Color </md-header-cell>
<md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
</md-table>
</div>
@@ -0,0 +1,116 @@
import {Component, ElementRef, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {MdSort} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/observable/fromEvent';
@Component({
selector: 'table-filtering-example',
styleUrls: ['table-filtering-example.css'],
templateUrl: 'table-filtering-example.html',
})
export class TableFilteringExample {
displayedColumns = ['userId', 'userName', 'progress', 'color'];
exampleDatabase = new ExampleDatabase();
dataSource: ExampleDataSource | null;
@ViewChild('filter') filter: ElementRef;
ngOnInit() {
this.dataSource = new ExampleDataSource(this.exampleDatabase);
Observable.fromEvent(this.filter.nativeElement, 'keyup')
.debounceTime(150)
.distinctUntilChanged()
.subscribe(() => {
if (!this.dataSource) { return; }
this.dataSource.filter = this.filter.nativeElement.value;
});
}
}
/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
export interface UserData {
id: string;
name: string;
progress: string;
color: string;
}
/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
/** Stream that emits whenever the data has been modified. */
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
get data(): UserData[] { return this.dataChange.value; }
constructor() {
// Fill up the database with 100 users.
for (let i = 0; i < 100; i++) { this.addUser(); }
}
/** Adds a new user to the database. */
addUser() {
const copiedData = this.data.slice();
copiedData.push(this.createNewUser());
this.dataChange.next(copiedData);
}
/** Builds and returns a new User. */
private createNewUser() {
const name =
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
return {
id: (this.data.length + 1).toString(),
name: name,
progress: Math.round(Math.random() * 100).toString(),
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
};
}
}
/**
* Data source to provide what data should be rendered in the table. Note that the data source
* can retrieve its data in any way. In this case, the data source is provided a reference
* to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
* the underlying data. Instead, it only needs to take the data and send the table exactly what
* should be rendered.
*/
export class ExampleDataSource extends DataSource<any> {
_filterChange = new BehaviorSubject('');
get filter(): string { return this._filterChange.value; }
set filter(filter: string) { this._filterChange.next(filter); }
constructor(private _exampleDatabase: ExampleDatabase) {
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<UserData[]> {
const displayDataChanges = [
this._exampleDatabase.dataChange,
this._filterChange,
];
return Observable.merge(...displayDataChanges).map(() => {
return this._exampleDatabase.data.slice().filter((item: UserData) => {
let searchStr = (item.name + item.color).toLowerCase();
return searchStr.indexOf(this.filter.toLowerCase()) != -1;
});
});
}
disconnect() {}
}
@@ -0,0 +1,57 @@
/* Structure */
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
background: white;
min-width: 300px;
}
.example-header {
min-height: 56px;
max-height: 56px;
display: flex;
align-items: center;
padding: 8px 24px 0;
font-size: 20px;
justify-content: space-between;
border-bottom: 1px solid transparent;
}
.mat-input-container {
font-size: 14px;
flex-grow: 1;
margin-left: 32px;
margin-top: 8px;
}
.example-no-results {
display: flex;
justify-content: center;
padding: 24px;
font-size: 12px;
font-style: italic;
}
/** Selection styles */
.example-selection-header {
font-size: 18px;
background: rgba(255, 64, 129, 0.3);
border-bottom: 1px solid #d696ac;
}
.mat-column-select {
max-width: 54px;
}
.mat-row:hover, .example-selected-row {
background: #f5f5f5;
}
.mat-row:active, .mat-row.example-selected-row {
background: #eaeaea;
}
.mat-table {
overflow: auto;
}
@@ -0,0 +1,79 @@
<div class="example-container mat-elevation-z8">
<div class="example-header" [style.display]="selection.isEmpty() ? '' : 'none'">
Users
<md-input-container floatPlaceholder="never">
<input mdInput #filter placeholder="Filter users">
</md-input-container>
</div>
<div class="example-header example-selection-header"
*ngIf="!selection.isEmpty()">
{{selection.selected.length}}
{{selection.selected.length == 1 ? 'user' : 'users'}}
selected
</div>
<md-table #table [dataSource]="dataSource" mdSort>
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- Checkbox Column -->
<ng-container cdkColumnDef="select">
<md-header-cell *cdkHeaderCellDef>
<md-checkbox (change)="$event ? masterToggle() : null"
[checked]="isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</md-checkbox>
</md-header-cell>
<md-cell *cdkCellDef="let row">
<md-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row.id) : null"
[checked]="selection.isSelected(row.id)">
</md-checkbox>
</md-cell>
</ng-container>
<!-- ID Column -->
<ng-container cdkColumnDef="userId">
<md-header-cell *cdkHeaderCellDef md-sort-header> ID </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
</ng-container>
<!-- Progress Column -->
<ng-container cdkColumnDef="progress">
<md-header-cell *cdkHeaderCellDef md-sort-header> Progress </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.progress}}% </md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="userName">
<md-header-cell *cdkHeaderCellDef md-sort-header> Name </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
</ng-container>
<!-- Color Column -->
<ng-container cdkColumnDef="color">
<md-header-cell *cdkHeaderCellDef md-sort-header> Color </md-header-cell>
<md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"
[class.example-selected-row]="selection.isSelected(row.id)"
(click)="selection.toggle(row.id)">
</md-row>
</md-table>
<div class="example-no-results"
[style.display]="dataSource.renderedData.length == 0 ? '' : 'none'">
No users found matching filter.
</div>
<md-paginator #paginator
[length]="dataSource.filteredData.length"
[pageIndex]="0"
[pageSize]="25"
[pageSizeOptions]="[5, 10, 25, 100]">
</md-paginator>
</div>
@@ -0,0 +1,181 @@
import {Component, ElementRef, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {MdPaginator, MdSort, SelectionModel} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/debounceTime';
@Component({
selector: 'table-overview-example',
styleUrls: ['table-overview-example.css'],
templateUrl: 'table-overview-example.html',
})
export class TableOverviewExample {
displayedColumns = ['select', 'userId', 'userName', 'progress', 'color'];
exampleDatabase = new ExampleDatabase();
selection = new SelectionModel<string>(true, []);
dataSource: ExampleDataSource | null;
@ViewChild(MdPaginator) paginator: MdPaginator;
@ViewChild(MdSort) sort: MdSort;
@ViewChild('filter') filter: ElementRef;
ngOnInit() {
this.dataSource = new ExampleDataSource(this.exampleDatabase, this.paginator, this.sort);
Observable.fromEvent(this.filter.nativeElement, 'keyup')
.debounceTime(150)
.distinctUntilChanged()
.subscribe(() => {
if (!this.dataSource) { return; }
this.dataSource.filter = this.filter.nativeElement.value;
});
}
isAllSelected(): boolean {
if (!this.dataSource) { return false; }
if (this.selection.isEmpty()) { return false; }
if (this.filter.nativeElement.value) {
return this.selection.selected.length == this.dataSource.renderedData.length;
} else {
return this.selection.selected.length == this.exampleDatabase.data.length;
}
}
masterToggle() {
if (!this.dataSource) { return; }
if (this.isAllSelected()) {
this.selection.clear();
} else if (this.filter.nativeElement.value) {
this.dataSource.renderedData.forEach(data => this.selection.select(data.id));
} else {
this.exampleDatabase.data.forEach(data => this.selection.select(data.id));
}
}
}
/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
export interface UserData {
id: string;
name: string;
progress: string;
color: string;
}
/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
/** Stream that emits whenever the data has been modified. */
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
get data(): UserData[] { return this.dataChange.value; }
constructor() {
// Fill up the database with 100 users.
for (let i = 0; i < 100; i++) { this.addUser(); }
}
/** Adds a new user to the database. */
addUser() {
const copiedData = this.data.slice();
copiedData.push(this.createNewUser());
this.dataChange.next(copiedData);
}
/** Builds and returns a new User. */
private createNewUser() {
const name =
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
return {
id: (this.data.length + 1).toString(),
name: name,
progress: Math.round(Math.random() * 100).toString(),
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
};
}
}
/**
* Data source to provide what data should be rendered in the table. Note that the data source
* can retrieve its data in any way. In this case, the data source is provided a reference
* to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
* the underlying data. Instead, it only needs to take the data and send the table exactly what
* should be rendered.
*/
export class ExampleDataSource extends DataSource<any> {
_filterChange = new BehaviorSubject('');
get filter(): string { return this._filterChange.value; }
set filter(filter: string) { this._filterChange.next(filter); }
filteredData: UserData[] = [];
renderedData: UserData[] = [];
constructor(private _exampleDatabase: ExampleDatabase,
private _paginator: MdPaginator,
private _sort: MdSort) {
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<UserData[]> {
// Listen for any changes in the base data, sorting, filtering, or pagination
const displayDataChanges = [
this._exampleDatabase.dataChange,
this._sort.mdSortChange,
this._filterChange,
this._paginator.page,
];
return Observable.merge(...displayDataChanges).map(() => {
// Filter data
this.filteredData = this._exampleDatabase.data.slice().filter((item: UserData) => {
let searchStr = (item.name + item.color).toLowerCase();
return searchStr.indexOf(this.filter.toLowerCase()) != -1;
});
// Sort filtered data
const sortedData = this.sortData(this.filteredData.slice());
// Grab the page's slice of the filtered sorted data.
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
this.renderedData = sortedData.splice(startIndex, this._paginator.pageSize);
return this.renderedData;
});
}
disconnect() {}
/** Returns a sorted copy of the database data. */
sortData(data: UserData[]): UserData[] {
if (!this._sort.active || this._sort.direction == '') { return data; }
return data.sort((a, b) => {
let propertyA: number|string = '';
let propertyB: number|string = '';
switch (this._sort.active) {
case 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
case 'userName': [propertyA, propertyB] = [a.name, b.name]; break;
case 'progress': [propertyA, propertyB] = [a.progress, b.progress]; break;
case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
}
let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
let valueB = isNaN(+propertyB) ? propertyB : +propertyB;
return (valueA < valueB ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1);
});
}
}
@@ -0,0 +1,20 @@
/* Structure */
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
background: white;
min-width: 300px;
}
.example-header {
min-height: 64px;
display: flex;
align-items: center;
padding-left: 24px;
font-size: 20px;
}
.mat-table {
overflow: auto;
}
@@ -0,0 +1,43 @@
<div class="example-container mat-elevation-z8">
<div class="example-header"> Users </div>
<md-table #table [dataSource]="dataSource">
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- ID Column -->
<ng-container cdkColumnDef="userId">
<md-header-cell *cdkHeaderCellDef> ID </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
</ng-container>
<!-- Progress Column -->
<ng-container cdkColumnDef="progress">
<md-header-cell *cdkHeaderCellDef> Progress </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.progress}}% </md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="userName">
<md-header-cell *cdkHeaderCellDef> Name </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
</ng-container>
<!-- Color Column -->
<ng-container cdkColumnDef="color">
<md-header-cell *cdkHeaderCellDef> Color </md-header-cell>
<md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
</md-table>
<md-paginator #paginator
[length]="exampleDatabase.data.length"
[pageIndex]="0"
[pageSize]="25"
[pageSizeOptions]="[5, 10, 25, 100]">
</md-paginator>
</div>
@@ -0,0 +1,103 @@
import {Component, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {MdPaginator} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
@Component({
selector: 'table-pagination-example',
styleUrls: ['table-pagination-example.css'],
templateUrl: 'table-pagination-example.html',
})
export class TablePaginationExample {
displayedColumns = ['userId', 'userName', 'progress', 'color'];
exampleDatabase = new ExampleDatabase();
dataSource: ExampleDataSource | null;
@ViewChild(MdPaginator) paginator: MdPaginator;
ngOnInit() {
this.dataSource = new ExampleDataSource(this.exampleDatabase, this.paginator);
}
}
/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
export interface UserData {
id: string;
name: string;
progress: string;
color: string;
}
/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
/** Stream that emits whenever the data has been modified. */
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
get data(): UserData[] { return this.dataChange.value; }
constructor() {
// Fill up the database with 100 users.
for (let i = 0; i < 100; i++) { this.addUser(); }
}
/** Adds a new user to the database. */
addUser() {
const copiedData = this.data.slice();
copiedData.push(this.createNewUser());
this.dataChange.next(copiedData);
}
/** Builds and returns a new User. */
private createNewUser() {
const name =
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
return {
id: (this.data.length + 1).toString(),
name: name,
progress: Math.round(Math.random() * 100).toString(),
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
};
}
}
/**
* Data source to provide what data should be rendered in the table. Note that the data source
* can retrieve its data in any way. In this case, the data source is provided a reference
* to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
* the underlying data. Instead, it only needs to take the data and send the table exactly what
* should be rendered.
*/
export class ExampleDataSource extends DataSource<any> {
constructor(private _exampleDatabase: ExampleDatabase, private _paginator: MdPaginator) {
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<UserData[]> {
const displayDataChanges = [
this._exampleDatabase.dataChange,
this._paginator.page,
];
return Observable.merge(...displayDataChanges).map(() => {
const data = this._exampleDatabase.data.slice();
// Grab the page's slice of data.
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
return data.splice(startIndex, this._paginator.pageSize);
});
}
disconnect() {}
}
@@ -0,0 +1,24 @@
/* Structure */
.example-container {
display: flex;
flex-direction: column;
max-height: 500px;
background: white;
min-width: 300px;
}
.example-header {
min-height: 64px;
display: flex;
align-items: center;
padding-left: 24px;
font-size: 20px;
}
.mat-table {
overflow: auto;
}
.mat-header-cell .mat-sort-header-sorted {
color: black;
}
@@ -0,0 +1,36 @@
<div class="example-container mat-elevation-z8">
<div class="example-header"> Users </div>
<md-table #table [dataSource]="dataSource" mdSort>
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- ID Column -->
<ng-container cdkColumnDef="userId">
<md-header-cell *cdkHeaderCellDef md-sort-header> ID </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
</ng-container>
<!-- Progress Column -->
<ng-container cdkColumnDef="progress">
<md-header-cell *cdkHeaderCellDef md-sort-header> Progress </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.progress}}% </md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="userName">
<md-header-cell *cdkHeaderCellDef md-sort-header> Name </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
</ng-container>
<!-- Color Column -->
<ng-container cdkColumnDef="color">
<md-header-cell *cdkHeaderCellDef md-sort-header> Color </md-header-cell>
<md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
</md-table>
</div>
@@ -0,0 +1,122 @@
import {Component, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {MdSort} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
@Component({
selector: 'table-sorting-example',
styleUrls: ['table-sorting-example.css'],
templateUrl: 'table-sorting-example.html',
})
export class TableSortingExample {
displayedColumns = ['userId', 'userName', 'progress', 'color'];
exampleDatabase = new ExampleDatabase();
dataSource: ExampleDataSource | null;
@ViewChild(MdSort) sort: MdSort;
ngOnInit() {
this.dataSource = new ExampleDataSource(this.exampleDatabase, this.sort);
}
}
/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];
export interface UserData {
id: string;
name: string;
progress: string;
color: string;
}
/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
/** Stream that emits whenever the data has been modified. */
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
get data(): UserData[] { return this.dataChange.value; }
constructor() {
// Fill up the database with 100 users.
for (let i = 0; i < 100; i++) { this.addUser(); }
}
/** Adds a new user to the database. */
addUser() {
const copiedData = this.data.slice();
copiedData.push(this.createNewUser());
this.dataChange.next(copiedData);
}
/** Builds and returns a new User. */
private createNewUser() {
const name =
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
return {
id: (this.data.length + 1).toString(),
name: name,
progress: Math.round(Math.random() * 100).toString(),
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
};
}
}
/**
* Data source to provide what data should be rendered in the table. Note that the data source
* can retrieve its data in any way. In this case, the data source is provided a reference
* to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
* the underlying data. Instead, it only needs to take the data and send the table exactly what
* should be rendered.
*/
export class ExampleDataSource extends DataSource<any> {
constructor(private _exampleDatabase: ExampleDatabase, private _sort: MdSort) {
super();
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<UserData[]> {
const displayDataChanges = [
this._exampleDatabase.dataChange,
this._sort.mdSortChange,
];
return Observable.merge(...displayDataChanges).map(() => {
return this.getSortedData();
});
}
disconnect() {}
/** Returns a sorted copy of the database data. */
getSortedData(): UserData[] {
const data = this._exampleDatabase.data.slice();
if (!this._sort.active || this._sort.direction == '') { return data; }
return data.sort((a, b) => {
let propertyA: number|string = '';
let propertyB: number|string = '';
switch (this._sort.active) {
case 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
case 'userName': [propertyA, propertyB] = [a.name, b.name]; break;
case 'progress': [propertyA, propertyB] = [a.progress, b.progress]; break;
case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
}
let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
let valueB = isNaN(+propertyB) ? propertyB : +propertyB;
return (valueA < valueB ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1);
});
}
}
+2 -1
View File
@@ -20,7 +20,8 @@
"types": [],
"baseUrl": ".",
"paths": {
"@angular/material": ["../../dist/packages/material/public_api"]
"@angular/material": ["../../dist/packages/material/public_api"],
"@angular/cdk": ["../../dist/packages/cdk/public_api"]
}
},
"files": [
+1 -1
View File
@@ -3,7 +3,7 @@ import {createPackageBuildTasks} from 'material2-build-tools';
// Create gulp tasks to build the different packages in the project.
createPackageBuildTasks('cdk');
createPackageBuildTasks('material', ['cdk']);
createPackageBuildTasks('material-examples', ['material']);
createPackageBuildTasks('material-examples', ['material', 'cdk']);
import './tasks/ci';
import './tasks/clean';
+1 -1
View File
@@ -69,7 +69,7 @@ task('docs', [
/** Generates html files from the markdown overviews and guides. */
task('markdown-docs', () => {
return src(['src/lib/**/*.md', 'guides/*.md'])
return src(['src/lib/**/*.md', 'src/cdk/**/*.md', 'guides/*.md'])
.pipe(markdown({
// Add syntax highlight using highlight.js
highlight: (code: string, language: string) => {