Skip to content

docs(table): updated docs for the table #8162

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

Closed
wants to merge 6 commits into from
Closed
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
179 changes: 157 additions & 22 deletions src/lib/table/table.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,185 @@
The `mat-table` provides a Material Design styled data-table that can be used to display rows of
data.
The `mat-table` provides a Material Design styled data-table that can be used to display rows of
data.

The table's template is made up of column definitions where each definition provides the content for
that column's header and row cells. It also includes a `<mat-header-row>` and `<mat-row>` where each
takes an ordered list of the columns they should render.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rephrase this paragraph a bit

The table's template consists of two parts: column definitions and row
definitions. Each column definition contains templates for that column's
header and content cells. Each row definition captures the columns used
for that row and any bindings applied to the row element.


Passing data to the table must be done through a `DataSource`, which connects to the table by
providing an Observable that emits an array of data. The table picks up this data array and writes
a `mat-row` for each data object in the array. It is the responsibility of the `DataSource` to send
exactly what data should be rendered by the table, so any data manipulation features such as
sorting or filtering should be implemented here.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to tighten the language a bit, avoiding passive voice:

A `DataSource` provides data to the table by emitting an `Observable` stream of
the items to be rendered. Each emit includes the _entire set of items_ that being
displayed. The table, listening to this stream, will render a row per item. Any
manipulation of the data being displayed (e.g. sorting, pagination, filtering)
should be captured by the `DataSource`, ultimately emitting a new set of
items to reflect any changes.


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 and attribute selectors will be prefixed
with `mat-` instead of `cdk-`.

For more information on the interface and how it works, see the
with `mat-` instead of `cdk-`. For detailed information on the interface and how it works, see the
[guide covering the CDK data-table](https://material.angular.io/guide/cdk-table).

### Getting Started

<!-- example(table-basic) -->

### Features
Start by writing your table's column definitions. Each column definition should be given a unique
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would numbered subheaders help here?

#### 1. Define the table's columns
...
#### 2. Define the table's rows
...
#### 3. Provide data
...

name and contain the content definitions for its header and row cells.

Here's a simple column definition with the name `'userName'`. The header cell contains the text
"Name" and each row cell will render the `name` property of each row's data.

```html
<ng-container matColumnDef="userName">
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.name}} </mat-cell>
</ng-container>
```

After you define all your columns, you'll need to provide the header and data row templates that
will be rendered out by the table. Each template should be given an ordered list of columns that
will let the table know what columns each row should render.

```html
<mat-header-row *matHeaderRowDef="['userName', 'age']"></mat-header-row>
<mat-row *matRowDef="let myRowData; columns: ['userName', 'age']"></mat-row>
```

Once your template is set up, the final step is to provide data to the table. This must be done
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something like

The column and row definitions now capture _how_ data will render- all that's
left is to provide the data itself. For simple scenarios with client-side operations,
`MatTableDataSource` offers a quick and easy starting point. Simply create an
instance of `MatTableDataSource` and set the items to be displayed to the
`data` property. For more advanced scenarios, applications will implement
one or more custom `DataSource` to capture more specific behaviors.

through a `DataSource`, which uses an Observable stream that emits an array of data that the table
should render.

To get started, you can use the pre-built `MatTableDataSource` to handle the stream for you.
Just create a new instance and set its `data` property to whatever data you want the table to
render.

The `<mat-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.
```ts
this.myDataSource = new MatTableDataSource();
this.myDataSource.data = dataToRender;
```

```html
<mat-table [dataSource]=”myDataSource”>
...
</mat-table>
```

### Features

#### Pagination

The `<mat-paginator>` adds a pagination UI that can be used in conjunction with the `<mat-table>`. The
paginator emits events that can be used to trigger an update via the table's data source.
To paginate the table's data, add a `<mat-paginator>` after the `<mat-table>` and provide the
`MatPaginator` to the `MatTableDataSource`. The data source will automatically listen for page
changes made by the user and send the right paged data to the table.

For more information on using and configuring the `<mat-paginator>`, check out the
[mat-paginator docs](https://material.angular.io/components/paginator/overview).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should mention here that mat-paginator is just one option. The table can work with any paginator control or any pagination strategy.

(same for sorting, filtering, and selection)


<!-- example(table-pagination) -->

#### Sorting

Use the `matSort` directive and `<mat-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.
To add sorting behavior to the table, add the `matSort` directive to the `<mat-table>` and add
`mat-sort-header` to each column header cell that should trigger sorting. Provide the `MatSort`
directive to the `MatTableDataSource` and it will automatically listen for sorting changes and
change the order of data rendered by the table.

By default, the `MatTableDataSource` sorts with the assumption that the sorted column's name
matches the data property name that the column displays. For example, the following column
definition is named `position`, which matches the name of the property displayed in the row cell.

```html
<!-- Name Column -->
<ng-container matColumnDef="position">
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.position}} </mat-cell>
</ng-container>
```

If the data properties do not match the column names, or if a more complex data property accessor
is required, then a custom `sortingDataAccessor` function can be set to override the default data
accessor on the `MatTableDataSource`.

<!-- example(table-sorting) -->

For more information on using and configuring the sorting behavior, check out the
[matSort docs](https://material.angular.io/components/sort/overview).

#### 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.
To remove filtered rows from the table's data, simply provide the filter string to the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is too specifically about the MatTableDataSource. We should mention that we don't provide any filtering UI components and that filtering is up to the app. Then MatTableDataSource can be the "if you want something simple" extra thing.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flexibility is great, but this documentation is under angular/material2. Most developers won't have time to try a lot of different options. Would still be good to provide example code for MatTableDataSource.

`MatTableDataSource`. The data source will reduce each row data to a serialized form and will
filter out the row if it does not contain the filter string. By default, the row data reducing
function will concatenate all the object values and convert them to lowercase.

<!--- example(table-filtering) -->
For example, the data object `{id: 123, name: 'Mr. Smith', favoriteColor: 'blue'}` will be
reduced to `123mr. smithblue`. If your filter string was `blue` then it would be considered a match
because it is contained in the reduced string, and the row would be displayed in the table.

### Simple Table
To override the default filtering behavior, a custom `filterPredicate` function can be set
which takes a data object and filter string and returns true if the data object is considered a
match.

<!--- example(table-filtering) -->

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).
#### Selection

To add row selection to the table, first set up a `SelectionModel` from `@angular/cdk/collections`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here about specificity- I want to keep it clear that users can do selection however they want. If we present it as "this is the way you are supposed to do selection", it begs the question as to why it's not just built in. I would really rather include this in an example with explanation that say "Here is the One True Way to do selection" (same for filtering)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flexibility is nice, but most developers are just going to want a working example they can start with.

that will maintain the selection state.

```js
const initialSelection = [];
const allowMultiSelect = true;
this.selection = new SelectionModel<MyDataType>(allowMultiSelect, initialSelection);
```

Add a column definition for displaying the row checkboxes, including a master toggle checkbox for
the header. The column name should be added to the list of displayed columns provided to the
`<mat-header-row>` and `<mat-row>`.

```html
<ng-container matColumnDef="select">
<mat-header-cell *matHeaderCellDef>
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</mat-header-cell>
<mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)">
</mat-checkbox>
</mat-cell>
</ng-container>
```

Implement the behavior in your component's logic to handle the header's master toggle and checking
if all rows are selected.

```js
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected == numRows;
}

/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
```

Finally, adjust the styling for the select column so that its overflow is not hidden. This allows
the ripple effect to extend beyond the cell.

```css
.mat-column-select {
overflow: initial;
}
```

<!--- example(table-selection) -->

### Accessibility
Tables without text or labels should be given a meaningful label via `aria-label` or
Expand Down