基于 Angular 的 Material Design 数据表格不完全指南

发表于 · 归类于 代码 · 阅读完需 10 分钟 · 报告错误

在 Web 应用,尤其是中后台应用中,数据表格(Data Table)是处理批量数据最有效、最常用的组件。本文将介绍 Angular Material 中数据表格的基本及进阶用法。

一、定义表格的模板

本文中,假设我们要实现一个展示用户列表的页面。所以首先,使用下面的命令生成 UsersComponent 组件:

$ ng g component users

以上命令会生成 UsersComponent 组件的 HTML 模板、CSS 样式、TypeScript 及单元测试等四个文件。

首先,在 HTML 模板文件中加入下面的列模板和行模板代码:

<mat-table [dataSource]="dataSource">
  <!-- Define the column templates -->
  <ng-container cdkColumnDef="username">
    <mat-header-cell *cdkHeaderCellDef> User name </mat-header-cell>
    <mat-cell *cdkCellDef="let row">  </mat-cell>
  </ng-container>

  <ng-container cdkColumnDef="age">
    <mat-header-cell *cdkHeaderCellDef> Age </mat-header-cell>
    <mat-cell *cdkCellDef="let row">  </mat-cell>
  </ng-container>

  <ng-container cdkColumnDef="title">
    <mat-header-cell *cdkHeaderCellDef> Title </mat-header-cell>
    <mat-cell *cdkCellDef="let row">  </mat-cell>
  </ng-container>

  <!-- Define the row templates -->
  <mat-header-row *cdkHeaderRowDef="['username', 'age', 'title']"></mat-header-row>
  <mat-row *cdkRowDef="let row; columns: ['username', 'age', 'title']"></mat-row>
</mat-table>

上面的代码,定义了 Table 中的 usernameagetitle 三个列模板,以及包含要渲染的列数组的行模板。

从上面的代码中也可以看到,我们会使用 dataSource 属性将数据传递给 mat-table 表格组件,以便进行视图的渲染。

二、数据源 MatTableDataSource

Angular Material 库自带了一个 MatTableDataSource 数据源,通过它可以实现对数组型数据的分页、排序和过滤功能。

在 HTML 模板文件中,我们增加分页组件:

<mat-table>
  ...
</mat-table>
<mat-paginator [pageSizeOptions]="[10, 20]" showFirstLastButtons></mat-paginator>

把模板中定义的 MatPaginator 提供给这个数据源,它将会自动监听用户所做的页码变更,并把正确分页之后的数据发给表格。具体代码如下:

export class UsersComponent implements OnInit {
  // 创建一个接口类型为 User 的 MatTableDataSource 数据源对象,其中 users 为数组型用户信息
  dataSource = new MatTableDataSource<User>(users);
  @ViewChild(MatPaginator) paginator: MatPaginator;

  constructor() {
    this.dataSource.paginator = this.paginator;
  }
}

这样,我们就实现一个简单的带分页的表格了。

三、自定义数据源

在大部分实际应用中,数据的分页和过滤一般由后端服务来实现。因此我们不能直接使用上一节中的方式来实现,而应该通过实现一个 DataSource 类来完成相应的分页、过滤和数据接受等逻辑。

DataSource 是一个拥有两个函数的基类:connectdisconnect。表格会调用 connect 函数,以接收一个流,流中会发出要渲染的数组型数据。当表格销毁时,就会调用 disconnect,它是清理 connect 期间所做的各种订阅的最佳时机。

export class UserDataSource extends DataSource<User> {
  // 用于关联分页组件
  public paginator: MatPaginator;

  private filterSubject = new BehaviorSubject<any>(null);
  private dataSubject = new BehaviorSubject<User[]>([]);
  private dataCountSubject = new BehaviorSubject<number>(0);
  private loadingSubject = new BehaviorSubject<boolean>(true);

  constructor(
    private userService: UserService
  ) {
    super();
  }
  // 获取当前用户数据的长度
  get dataCount(): number {
    return this.dataCountSubject.value;
  }
  // 用于加载中动画的显示
  get loading(): boolean {
    return this.loadingSubject.value;
  }

  get filter() {
    return this.filterSubject.value;
  }
  set filter(params: any) {
    this.filterSubject.next(params);
    this.paginator.pageIndex = 0;
  }

  loadData() {
    const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
    const pageSize = this.paginator.pageSize;

    // 传入相应的参数,从后端服务中获取用户数据
    this.userService.getUsers(startIndex, pageSize, this.filter).subscribe(result => {
      this.dataSubject.next(result['data']);
      this.dataCountSubject.next(result['total']);
      this.loadingSubject.next(false);
    });
  }

  connect(): Observable<User[]> {
    return this.dataSubject.asObservable();
  }

  disconnect() {
    this.dataSubject.complete();
    this.loadingSubject.complete();
  }
}

在 UsersComponent 组件中,调用 loadData 方法即可获取用户数据,并立即更新表格视图。