Skip to content

Commit 610e357

Browse files
committed
feat(table): enable native table selectors
1 parent 0e37d6c commit 610e357

31 files changed

+794
-202
lines changed

src/cdk/table/cell.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class CdkColumnDef {
6161

6262
/** Header cell template container that adds the right classes and role. */
6363
@Directive({
64-
selector: 'cdk-header-cell',
64+
selector: 'cdk-header-cell, th[cdk-header-cell]',
6565
host: {
6666
'class': 'cdk-header-cell',
6767
'role': 'columnheader',
@@ -75,7 +75,7 @@ export class CdkHeaderCell {
7575

7676
/** Cell template container that adds the right classes and role. */
7777
@Directive({
78-
selector: 'cdk-cell',
78+
selector: 'cdk-cell, td[cdk-cell]',
7979
host: {
8080
'class': 'cdk-cell',
8181
'role': 'gridcell',

src/cdk/table/row.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export class CdkCellOutlet {
151151
/** Header template container that contains the cell outlet. Adds the right class and role. */
152152
@Component({
153153
moduleId: module.id,
154-
selector: 'cdk-header-row',
154+
selector: 'cdk-header-row, tr[cdk-header-row]',
155155
template: CDK_ROW_TEMPLATE,
156156
host: {
157157
'class': 'cdk-header-row',
@@ -165,7 +165,7 @@ export class CdkHeaderRow { }
165165
/** Data row template container that contains the cell outlet. Adds the right class and role. */
166166
@Component({
167167
moduleId: module.id,
168-
selector: 'cdk-row',
168+
selector: 'cdk-row, tr[cdk-row]',
169169
template: CDK_ROW_TEMPLATE,
170170
host: {
171171
'class': 'cdk-row',

src/cdk/table/table-module.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
import {CommonModule} from '@angular/common';
1010
import {NgModule} from '@angular/core';
11-
import {HeaderRowPlaceholder, RowPlaceholder, CdkTable} from './table';
11+
import {CdkTable, HeaderRowPlaceholder, RowPlaceholder} from './table';
1212
import {CdkCellOutlet, CdkHeaderRow, CdkHeaderRowDef, CdkRow, CdkRowDef} from './row';
13-
import {CdkColumnDef, CdkHeaderCellDef, CdkHeaderCell, CdkCell, CdkCellDef} from './cell';
13+
import {CdkCell, CdkCellDef, CdkColumnDef, CdkHeaderCell, CdkHeaderCellDef} from './cell';
1414

1515
const EXPORTED_DECLARATIONS = [
1616
CdkTable,
@@ -30,8 +30,8 @@ const EXPORTED_DECLARATIONS = [
3030

3131
@NgModule({
3232
imports: [CommonModule],
33-
exports: [EXPORTED_DECLARATIONS],
34-
declarations: [EXPORTED_DECLARATIONS]
33+
exports: EXPORTED_DECLARATIONS,
34+
declarations: EXPORTED_DECLARATIONS
3535

3636
})
3737
export class CdkTableModule { }

src/cdk/table/table.md

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
The `<cdk-table>` is an unopinionated, customizable data-table with a fully-templated API, dynamic
1+
The `CdkTable` is an unopinionated, customizable data-table with a fully-templated API, dynamic
22
columns, and an accessible DOM structure. This component acts as the core upon which anyone can
33
build their own tailored data-table experience.
44

@@ -7,7 +7,7 @@ built. Because it enforces no opinions on these matters, developers have full co
77
interaction patterns associated with the table.
88

99
For a Material Design styled table, see the
10-
[documentation for `<mat-table>`](https://material.angular.io/components/table) which builds on
10+
[documentation for `MatTable`](https://material.angular.io/components/table) which builds on
1111
top of the CDK data-table.
1212

1313
<!-- example(cdk-table-basic) -->
@@ -23,8 +23,8 @@ the column a name. Each column definition then further defines both a header-cel
2323

2424
```html
2525
<ng-container cdkColumnDef="username">
26-
<cdk-header-cell *cdkHeaderCellDef> User name </cdk-header-cell>
27-
<cdk-cell *cdkCellDef="let row"> {{row.a}} </cdk-cell>
26+
<th cdk-header-cell *cdkHeaderCellDef> User name </th>
27+
<td cdk-cell *cdkCellDef="let row"> {{row.a}} </td>
2828
</ng-container>
2929
```
3030

@@ -38,8 +38,8 @@ last).
3838
The next step is to define the table's header-row (`cdkHeaderRowDef`) and data-row (`cdkRowDef`).
3939

4040
```html
41-
<cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></cdk-header-row>
42-
<cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></cdk-row>
41+
<tr cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></tr>
42+
<tr cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></tr>
4343
```
4444

4545
These row templates accept the specific columns to be rendered via the name given to the
@@ -53,58 +53,58 @@ above.
5353
##### Example: table with three columns
5454

5555
```html
56-
<cdk-table [dataSource]="dataSource">
56+
<table cdk-table [dataSource]="dataSource">
5757
<!-- User name Definition -->
5858
<ng-container cdkColumnDef="username">
59-
<cdk-header-cell *cdkHeaderCellDef> User name </cdk-header-cell>
60-
<cdk-cell *cdkCellDef="let row"> {{row.username}} </cdk-cell>
59+
<th cdk-header-cell *cdkHeaderCellDef> User name </th>
60+
<td cdk-cell *cdkCellDef="let row"> {{row.username}} </td>
6161
</ng-container>
6262

6363
<!-- Age Definition -->
6464
<ng-container cdkColumnDef="age">
65-
<cdk-header-cell *cdkHeaderCellDef> Age </cdk-header-cell>
66-
<cdk-cell *cdkCellDef="let row"> {{row.age}} </cdk-cell>
65+
<th cdk-header-cell *cdkHeaderCellDef> Age </th>
66+
<td cdk-cell *cdkCellDef="let row"> {{row.age}} </td>
6767
</ng-container>
6868

6969
<!-- Title Definition -->
7070
<ng-container cdkColumnDef="title">
71-
<cdk-header-cell *cdkHeaderCellDef> Title </cdk-header-cell>
72-
<cdk-cell *cdkCellDef="let row"> {{row.title}} </cdk-cell>
71+
<th cdk-header-cell *cdkHeaderCellDef> Title </th>
72+
<td cdk-cell *cdkCellDef="let row"> {{row.title}} </td>
7373
</ng-container>
7474

7575
<!-- Header and Row Declarations -->
76-
<cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></cdk-header-row>
77-
<cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></cdk-row>
78-
</cdk-table>
76+
<tr cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></tr>
77+
<tr cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></tr>
78+
</table>
7979
```
8080

8181
The columns given on the row determine which cells are rendered and in which order. Thus, the
8282
columns can be set via binding to support dynamically changing the columns shown at run-time.
8383

8484
```html
85-
<cdk-row *cdkRowDef="let row; columns: myDisplayedColumns"></cdk-row>
85+
<tr cdk-row *cdkRowDef="let row; columns: myDisplayedColumns"></tr>
8686
```
8787

8888
It is not required to display all the columns that are defined within the template,
8989
nor use the same ordering. For example, to display the table with only `age`
9090
and `username` and in that order, then the row and header definitions would be written as:
9191

9292
```html
93-
<cdk-row *cdkRowDef="let row; columns: ['age', 'username']"></cdk-row>
93+
<tr cdk-row *cdkRowDef="let row; columns: ['age', 'username']"></tr>
9494
```
9595

9696
Event and property bindings can be added directly to the row element.
9797

9898
##### Example: table with event and class binding
9999
```html
100-
<cdk-header-row *cdkHeaderRowDef="['age', 'username']"
101-
(click)="handleHeaderRowClick(row)">
102-
</cdk-header-row>
103-
104-
<cdk-row *cdkRowDef="let row; columns: ['age', 'username']"
105-
[class.can-vote]="row.age >= 18"
106-
(click)="handleRowClick(row)">
107-
</cdk-row>
100+
<tr cdk-header-row *cdkHeaderRowDef="['age', 'username']"
101+
(click)="handleHeaderRowClick(row)">
102+
</tr>
103+
104+
<tr cdk-row *cdkRowDef="let row; columns: ['age', 'username']"
105+
[class.can-vote]="row.age >= 18"
106+
(click)="handleRowClick(row)">
107+
</tr>
108108
```
109109

110110
##### Styling columns
@@ -136,5 +136,45 @@ To improve performance, a `trackBy` function can be provided to the table simila
136136
table how to uniquely identify rows to track how the data changes with each update.
137137

138138
```html
139-
<cdk-table [dataSource]="dataSource" [trackBy]="myTrackById">
139+
<table cdk-table [dataSource]="dataSource" [trackBy]="myTrackById">
140+
```
141+
142+
### Alternate HTML to using native table
143+
144+
The CDK table does not require that you use a native HTML table. If you want to have full control
145+
over the style of the table, it may be easier to follow an alternative template approach that does
146+
not use the native table element tags.
147+
148+
This alternative approach replaces the native table element tags with the CDK table directive
149+
selectors. For example, `<table cdk-table>` becomes `<cdk-table>`; `<tr cdk-row`> becomes
150+
`<cdk-row>`. The following shows a previous example using this alternative template:
151+
152+
```html
153+
<cdk-table [dataSource]="dataSource">
154+
<!-- User name Definition -->
155+
<ng-container cdkColumnDef="username">
156+
<cdk-header-cell *cdkHeaderCellDef> User name </cdk-header-cell>
157+
<cdk-cell *cdkCellDef="let row"> {{row.username}} </cdk-cell>
158+
</ng-container>
159+
160+
<!-- Age Definition -->
161+
<ng-container cdkColumnDef="age">
162+
<cdk-header-cell *cdkHeaderCellDef> Age </cdk-header-cell>
163+
<cdk-cell *cdkCellDef="let row"> {{row.age}} </cdk-cell>
164+
</ng-container>
165+
166+
<!-- Title Definition -->
167+
<ng-container cdkColumnDef="title">
168+
<cdk-header-cell *cdkHeaderCellDef> Title </cdk-header-cell>
169+
<cdk-cell *cdkCellDef="let row"> {{row.title}} </cdk-cell>
170+
</ng-container>
171+
172+
<!-- Header and Row Declarations -->
173+
<cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></cdk-header-row>
174+
<cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></cdk-row>
175+
</cdk-table>
140176
```
177+
178+
For an example of how to render the structure as a table, see the
179+
[documentation for `<mat-table>`](https://material.angular.io/components/table) which includes
180+
the style support for this approach.

src/cdk/table/table.spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,19 @@ describe('CdkTable', () => {
232232
});
233233
});
234234

235+
it('should render correctly when using native HTML tags', () => {
236+
const thisFixture = createComponent(NativeHtmlTableApp);
237+
const thisTableElement = thisFixture.nativeElement.querySelector('table');
238+
thisFixture.detectChanges();
239+
240+
expectTableToMatchContent(thisTableElement, [
241+
['Column A', 'Column B', 'Column C'],
242+
['a_1', 'b_1', 'c_1'],
243+
['a_2', 'b_2', 'c_2'],
244+
['a_3', 'b_3', 'c_3'],
245+
]);
246+
});
247+
235248
it('should render cells even if row data is falsy', () => {
236249
const booleanRowCdkTableAppFixture = createComponent(BooleanRowCdkTableApp);
237250
booleanRowCdkTableAppFixture.detectChanges();
@@ -1349,6 +1362,36 @@ class OuterTableApp {
13491362
firstRow = i => i === 0;
13501363
}
13511364

1365+
@Component({
1366+
template: `
1367+
<table cdk-table [dataSource]="dataSource">
1368+
<ng-container cdkColumnDef="column_a">
1369+
<th cdk-header-cell *cdkHeaderCellDef> Column A</th>
1370+
<td cdk-cell *cdkCellDef="let row"> {{row.a}}</td>
1371+
</ng-container>
1372+
1373+
<ng-container cdkColumnDef="column_b">
1374+
<th cdk-header-cell *cdkHeaderCellDef> Column B</th>
1375+
<td cdk-cell *cdkCellDef="let row"> {{row.b}}</td>
1376+
</ng-container>
1377+
1378+
<ng-container cdkColumnDef="column_c">
1379+
<th cdk-header-cell *cdkHeaderCellDef> Column C</th>
1380+
<td cdk-cell *cdkCellDef="let row"> {{row.c}}</td>
1381+
</ng-container>
1382+
1383+
<tr cdk-header-row *cdkHeaderRowDef="columnsToRender"></tr>
1384+
<tr cdk-row *cdkRowDef="let row; columns: columnsToRender" class="customRowClass"></tr>
1385+
</table>
1386+
`
1387+
})
1388+
class NativeHtmlTableApp {
1389+
dataSource: FakeDataSource | undefined = new FakeDataSource();
1390+
columnsToRender = ['column_a', 'column_b', 'column_c'];
1391+
1392+
@ViewChild(CdkTable) table: CdkTable<TestData>;
1393+
}
1394+
13521395
function getElements(element: Element, query: string): Element[] {
13531396
return [].slice.call(element.querySelectorAll(query));
13541397
}

src/cdk/table/table.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import {
4949
*/
5050
@Directive({selector: '[rowPlaceholder]'})
5151
export class RowPlaceholder {
52-
constructor(public viewContainer: ViewContainerRef) { }
52+
constructor(public viewContainer: ViewContainerRef, public elementRef: ElementRef) { }
5353
}
5454

5555
/**
@@ -58,7 +58,7 @@ export class RowPlaceholder {
5858
*/
5959
@Directive({selector: '[headerRowPlaceholder]'})
6060
export class HeaderRowPlaceholder {
61-
constructor(public viewContainer: ViewContainerRef) { }
61+
constructor(public viewContainer: ViewContainerRef, public elementRef: ElementRef) { }
6262
}
6363

6464
/**
@@ -83,7 +83,7 @@ abstract class RowViewRef<T> extends EmbeddedViewRef<CdkCellOutletRowContext<T>>
8383
*/
8484
@Component({
8585
moduleId: module.id,
86-
selector: 'cdk-table',
86+
selector: 'cdk-table, table[cdk-table]',
8787
exportAs: 'cdkTable',
8888
template: CDK_TABLE_TEMPLATE,
8989
host: {
@@ -212,14 +212,18 @@ export class CdkTable<T> implements CollectionViewer, OnInit, AfterContentChecke
212212

213213
constructor(private readonly _differs: IterableDiffers,
214214
private readonly _changeDetectorRef: ChangeDetectorRef,
215-
elementRef: ElementRef,
215+
private readonly _elementRef: ElementRef,
216216
@Attribute('role') role: string) {
217217
if (!role) {
218-
elementRef.nativeElement.setAttribute('role', 'grid');
218+
this._elementRef.nativeElement.setAttribute('role', 'grid');
219219
}
220220
}
221221

222222
ngOnInit() {
223+
if (this._elementRef.nativeElement.tagName === 'TABLE') {
224+
this._applyNativeTableSections();
225+
}
226+
223227
// TODO(andrewseguin): Setup a listener for scrolling, emit the calculated view to viewChange
224228
this._dataDiffer = this._differs.find([]).create(this._trackByFn);
225229

@@ -558,4 +562,16 @@ export class CdkTable<T> implements CollectionViewer, OnInit, AfterContentChecke
558562
return column.cell;
559563
});
560564
}
565+
566+
/** Adds native table sections (e.g. tbody) and moves the row placeholders into them. */
567+
private _applyNativeTableSections() {
568+
const tHeadEl = document.createElement('thead');
569+
const tBodyEl = document.createElement('tbody');
570+
571+
this._elementRef.nativeElement.appendChild(tHeadEl);
572+
this._elementRef.nativeElement.appendChild(tBodyEl);
573+
574+
tHeadEl.appendChild(this._headerRowPlaceholder.elementRef.nativeElement);
575+
tBodyEl.appendChild(this._rowPlaceholder.elementRef.nativeElement);
576+
}
561577
}

0 commit comments

Comments
 (0)