异步初始化MatPaginator

1. 问题描述

我们在组件中初始化MatPaginator,如

@ViewChild(MatPaginator) paginator: MatPaginator;

并将其分配给在我们的组件中定义的MatTable DataSource

matTableDataSource.paginator=this.ginator;

当使用带有*ngIf组合的async管道异步初始化数据源时,这在用例中不起作用。

像这样:

1
2
3
4
5
6
7
8

<div *ngIf="someDataObs | async as yourData else loading">
<mat-table #table [dataSource]="yourData">
... your headers and columns
</mat-table>
<mat-paginator [pageSize]="10" showFirstLastButtons></mat-paginator>
</div>

注意:您可以使用*ngIf将mat-paginator放在div下面,但这不是一个理想的解决方案,如果您需要在同一组件中显示多个表,并使用单个后端异步调用,则更是如此。

由于mat-paginator被包裹在ngIf之类, 所以未从数据源获取到数据之前mat-paginator没有被初始化, 因此当我们创建matTableDataSource时paginator仍然为null。

1
2
3
4
5
6
7
8
ngAfterViewInit() {
this.someDataObs = this.backendService.get()
.map(value => {
const matTableDataSource = new MatTableDataSource<SomeType>(value);
matTableDataSource.paginator = this.paginator; // will be null here
return matTableDataSource;
});
}

2. 解决方案

所以要解决这个问题, 我们需要改变一下思路, 在创建matTableDataSource时我们不要急于为其设置paginator, 等mat-paginator初始化后再将mat-paginator关联至matTableDataSource.

在创建matTableDataSource时我们将为其关联paginator的代码移除

1
2
3
4
5
6
7
8
9
10
11
12

private _datasource: MatTableDataSource<SomeType>

ngAfterViewInit() {
this.someDataObs = this.backendService.get()
.map(value => {
const matTableDataSource = new MatTableDataSource<SomeType>(value);
// matTableDataSource.paginator = this.paginator; // 将matTableDataSource与paginator的代码注释掉
this._datasource = matTableDataSource // 这里需要创建一个_datasource用来持有matTableDataSource对象, 当paginator初始化完成后我们需要将paginator赋值给matTableDataSource
return matTableDataSource;
});
}

现在我们要找到paginator初始化后赋值的位置, 将paginator赋值给datasource对象。我们可以使用angular setter拦截赋值过程, 关于angular setter和getter可以参考我的博客在TypeScript和Angular中使用Getter和Setter, 这里不展开论述.

我们需要将paginator修改为使用setter赋值, 这样我们就有机会在paginator初始化以后将其关联到matTableDataSource

1
2
3
4
5

@ViewChild(MatPaginator, { static: false }) set paginator(mp: MatPaginator) {
this._datasource.paginator = mp
}

注意: 这里需要使用参数static: false
static若为 true,在变更检测运行之前解析查询结果;若为 false,在变更检测之后解析。默认为 false。

至此MatPaginator就能正常的运行了.

该解决方案也适用于sort不能异步初始化的情况. 加上如下一行代码就能解决了

1
2
3
4
5

@ViewChild(MatSort, { static: false }) set sort(ms: MatSort) {
this._datasource.sort = ms
}

3. 相关文章

最新更新以及更多Angular相关文章请访问 Angular合集 | 鹏叔的技术博客

4. 参考文档

MatPaginator in Angular fails to initialize

Initialising MatPaginator asynchronously

Mat Paginator is not working properly along when used conditional rendering (*ngIf) on the outer div.