Skip to content

Table docs #5449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions guides/cdk-table.md
Original file line number Diff line number Diff line change
@@ -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]
10 changes: 5 additions & 5 deletions src/demo-app/table/table-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ <h3>CdkTable Example</h3>
<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,
Expand All @@ -92,7 +92,7 @@ <h3>MdTable Example</h3>

<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>

Expand Down Expand Up @@ -130,8 +130,8 @@ <h3>MdTable Example</h3>
<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>

Expand Down
12 changes: 6 additions & 6 deletions src/demo-app/table/table-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>();
Expand All @@ -32,15 +32,15 @@ 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();
}

disconnect() {
this.dataSource = null;
this.propertiesToDisplay = [];
this.displayedColumns = [];
}

getOpacity(progress: number) {
Expand All @@ -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);
}
}

Expand Down
49 changes: 49 additions & 0 deletions src/lib/table/table.md
Original file line number Diff line number Diff line change
@@ -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).
39 changes: 39 additions & 0 deletions src/material-examples/cdk-table-basic/cdk-table-basic-example.css
Original file line number Diff line number Diff line change
@@ -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);
}
36 changes: 36 additions & 0 deletions src/material-examples/cdk-table-basic/cdk-table-basic-example.html
Original file line number Diff line number Diff line change
@@ -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>
Loading