CdkTable 是一个非自用的、可定制的数据表格格,它包含一个完全模板化的 API、动态列和一个可访问的 DOM 结构。这个组件充当了核心,任何人都可以在此基础上构建自己定制的数据表格格。

表格提供了一个基础,可以在这个基础上构建其它特性,比如排序和分页。由于它不强制要求这些琐事,因此开发人员可以完全控制与该表格有关的交互模式。

有关 Material Design 风格的表格,请参阅 MatTable 文档,它建立在 CDK 数据表格之上。

编写数据表格模板的第一步是定义列。列的定义是通过带有 cdkColumnDef 指令的 <ng-container> 来指定的,并赋予该列一个名字。每个列定义都可以包含一个表头单元格模板(cdkHeaderCellDef),一个数据单元格模板(cdkCellDef)和一个表尾单元格模板(cdkFooterCellDef)。

<ng-container cdkColumnDef="username">
  <th cdk-header-cell *cdkHeaderCellDef> User name </th>
  <td cdk-cell *cdkCellDef="let row"> {{row.a}} </td>
  <td cdk-footer-cell *cdkFooterCellDef> User name </td>
</ng-container>

列定义的集合表示可渲染的列。要渲染的特定列及其顺序可以在本行中指定(稍后讲)。

注意,cdkCellDef 导出了本行的上下文,以便在单元格模板中引用本行的数据。该指令还导出了一些与 ngFor 相同的属性(index,even,odd,first,last)。

下一步是定义表格的表头行(cdkHeaderRowDef)、数据行(cdkRowDef)和表尾行(cdkFooterRowDef)。注意,它们都是可选的,具体取决于你要渲染的行类型(例如,如果你不需要一个表尾行,那就不要添加它的定义)。

<tr cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></tr>
<tr cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></tr>
<tr cdk-footer-row *cdkFooterRowDef="['username', 'age', 'title']"></tr>

这些行模板通过赋值给 cdkColumnDef 的名字来接受要渲染的指定列。

cdkRowDef 也会导出行的上下文,它可以用在这个行元素的事件和属性绑定上。任何放在标题行或数据行模板中的内容都会被忽略,因为该行渲染的内容来自上面描述的单元格模板。

<table cdk-table [dataSource]="dataSource">
  <!-- User name Definition -->
  <ng-container cdkColumnDef="username">
    <th cdk-header-cell *cdkHeaderCellDef> User name </th>
    <td cdk-cell *cdkCellDef="let row"> {{row.username}} </td>
  </ng-container>

  <!-- Age Definition -->
  <ng-container cdkColumnDef="age">
    <th cdk-header-cell *cdkHeaderCellDef> Age </th>
    <td cdk-cell *cdkCellDef="let row"> {{row.age}} </td>
  </ng-container>

  <!-- Title Definition -->
  <ng-container cdkColumnDef="title">
    <th cdk-header-cell *cdkHeaderCellDef> Title </th>
    <td cdk-cell *cdkCellDef="let row"> {{row.title}} </td>
  </ng-container>

  <!-- Header and Row Declarations -->
  <tr cdk-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></tr>
  <tr cdk-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></tr>
</table>

本行给出的这些列决定要渲染哪些单元格以及按什么顺序。因此,可以通过绑定来设置列,以支持动态更改运行时要显示的列。

<tr cdk-row *cdkRowDef="let row; columns: myDisplayedColumns"></tr>

不需要包括显示模板中定义的所有列,也不需要使用与定义时相同的顺序。例如,要显示一个只包含 ageusername 的表格,那么行和头的定义就写成:

<tr cdk-row *cdkRowDef="let row; columns: ['age', 'username']"></tr>

事件和属性绑定可以直接添加到 row 元素上。

<tr cdk-header-row *cdkHeaderRowDef="['age', 'username']"
    (click)="handleHeaderRowClick(row)">
</tr>

<tr cdk-row *cdkRowDef="let row; columns: ['age', 'username']"
    [class.can-vote]="row.age >= 18"
    (click)="handleRowClick(row)">
</tr>

每个表头和数据行中的单元格都会被提供一个包含其列的 CSS 类。例如,name 列中显示的单元格将被赋予 cdk-column-name 类。这样就可以让列的样式在表头和数据行之间保持一致。

由于列的名字可以是任意字符串,所以它可能无法直接用在 CSS 类中(例如 *nameColumn! )。此时,这些特殊字符将替换成 - 字符。例如,*nameColumn! 列中的单元格容器将会带有 cdk-column--nameColumn- 类。

数据会通过 DataSource 提供给表格。当表格接收数据源时,它会调用 DataSource 的 connect() 方法,该方法返回一个发出数组型数据的可观察对象。每当数据源向此流中发出数据时,该表格都会重新渲染一次。

由于数据源提供了这个流,因此它要负责触发表格更新。这可能由任何事情触发:websocket 连接、用户交互、模型更新、基于时间间隔等。最常见的是,这些更新将由用户交互(如排序和分页)触发。

CDK 表会应用粘滞样式之前要先测量粘滞元素的尺寸。由于原生表格会根据每个单元格内的内容计算出列宽,因此没当基础数据发生变化时就会重新检查这些尺寸。

启用 fixedLayout 将强制统一列宽,这样表格就可以在计算粘滞样式时可靠地缓存和复用它们。这可以减少大型原生表格的渲染延迟。

<table cdk-table [dataSource]="dataSource" fixedLayout>

要想提高性能,可以在表格中提供一个类似于 Angular 的 ngFor trackBy 指令。这会告诉表格要如何唯一地标识这些行,用以跟踪每次更新后数据的变化情况。

<table cdk-table [dataSource]="dataSource" [trackBy]="myTrackById">

默认情况下, CdkTable 会为每一行创建和销毁一个内部 Angular 视图。这允许这些行参与动画并使用 cdkRowDefWhen 在不同的行模板之间切换。如果你不需要这些功能,可以通过指定 recycleRows 来指示表格缓存和回收这些行。

<table cdk-table [dataSource]="dataSource" recycleRows>

CDK 表格并不要求你使用原生 HTML 表格。如果你想完全控制表格的样式,遵循不使用原生表格元素标签的替代模板方法可能会更容易些。

这种替代方法用 CDK 的表格指令选择器替换了原生的表格元素标签。比如,<table cdk-table> 变为 <cdk-table><tr cdk-row > 变为 <cdk-row>。下面的例子展示了改用这个替代模板实现前一个例子:

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

有关如何将此结构渲染为表格的示例,请参阅 <mat-table>文档,其中包括此方法所需的样式支持。