没有setTimeOut

时间:2018-10-26 17:23:35

标签: javascript angular sorting pagination angular-material

我有一个来自端点的数据,并将其放入MatTableDataSource中。我能够使它适用于MatSort和MatPaginator,但需要使用setTimeOut,但这似乎不是执行此操作的正确方法。如果我删除了它,它将抱怨“无法读取未定义的排序属性”,我认为这是由于尚未初始化。

我也尝试过:

  • 将其移动到afterviewinit,但是数据是在之后加载的 afterviewinit被调用,所以它仍然不起作用
  • 在this.datasource =之后使用this.changeDetectorRef.detectChanges() 新的...也不起作用

这是我当前的代码(正在运行,但是使用settimeout)

<div *ngIf="!isLoading">
    <div *ngFor="let record of renderedData | async" matSort>

    // ... some html to show the 'record'

    <mat-paginator #paginator
        [pageSizeOptions]="[5, 10, 20]">
    </mat-paginator>
</div>

组件

export class ResultsComponent implements OnInit, OnDestroy, AfterViewInit {
    dataSource: MatTableDataSource<any> = new MatTableDataSource();
    renderedData: Observable<any>;

    @ViewChild(MatPaginator) paginator: MatPaginator;
    @ViewChild(MatSort) sort: MatSort;

    constructor(some service) {}

    ngOnInit() {
        const accountId = someOtherService.getAccountId();
        this.someService.getData(accountId)
            .subscribe((myData) => {
                    this.dataSource = new MatTableDataSource(myData);

                    // it won't work properly if it is not wrapped in timeout
                    setTimeout(() => {
                        this.dataSource.paginator = this.paginator;
                        this.sort.sort(<MatSortable>({id: 'created_on', start: 'desc'}));
                        this.dataSource.sort = this.sort;
                    });

                    this.renderedData = this.dataSource.connect();
                }
            });
    }

    ngAfterViewInit() {
    }

    ngOnDestroy(): void {
        if (this.dataSource) { this.dataSource.disconnect(); }
    }
}

上面的代码对我有用,我只是在寻找正确的方法,如果可能的话,不要使用settimeout。

2 个答案:

答案 0 :(得分:2)

这里有几个生命周期计时问题在起作用,当您考虑它时,这是正确的。

MatSort是视图的一部分,因此在OnInit期间尚未“就绪”-未定义。因此,尝试使用它会引发错误。

MatSort已在AfterViewInit中准备好,但是由于您需要在执行排序后将排序“应用”到数据源,并且通过“ renderedData”触发对视图的更改,使事情变得更加复杂。已连接到数据源。因此,由于视图初始化生命周期尚未完成,但您已经在尝试对其进行更改,因此最终会遇到ExpressionChangedAfterItHaHasBeenCheckedError。

因此,在视图准备就绪之前,您无法进行排序,并且在通知您视图已准备就绪时也无法应用排序。您唯一可以做的就是等待组件初始化生命周期的结束。您可以使用setTimeout()做到这一点。

如果没有setTimeout(),我认为这两个问题都无法解决,因此在这种情况下,从OnInit还是AfterViewInit调用它都没有关系。

关于代码的其他一些观察结果:

您无需在订阅中创建MatTableDataSource的新实例。您可以将结果数据分配给已创建的数据源:

this.dataSource.data = myData;

这使您不必再将渲染的数据连接到数据源,因此可以在初始化数据源时做到这一点:

dataSource: MatTableDataSource<any> = new MatTableDataSource();
renderedData: Observable<any> = this.dataSource.connect();

答案 1 :(得分:0)

实际上有一种不使用setTimeout的方法。该功能适用​​于Angular 9,但我尚未在以前的版本中对其进行过测试,因此我不确定它是否可以在其中使用。

我在这里找到了此解决方案:https://github.com/angular/components/issues/10205

代替放置:

<table>
  <tbody> <!-- thead, or tfoot -->
   <tr>

为matSort使用setter。模板中的matSort更改后(即首次定义),此设置器将触发,当您通过单击箭头更改排序时,该设置器将不触发。看起来像这样:

@ViewChild(MatSort) sort: MatSort;

您可以对分页器使用相同的解决方案。