mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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:
@@ -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]
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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": [
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user