MatSort 和 MatPaginator 在没有 setTimeOut 的情况下不起作用

Har*_*rts 4 javascript sorting pagination angular-material angular

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

我也试过:

  • 将其移动到afterviewinit,但数据是在afterviewinit被调用后加载的,所以它仍然不起作用
  • 在 this.datasource = new 之后使用 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>
Run Code Online (Sandbox Code Playgroud)

组件

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(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

G. *_*ter 5

这里有几个生命周期时间问题,当你考虑它时,这是正确的。

MatSort 是视图的一部分,因此在 OnInit 期间它没有“准备好”——它是未定义的。所以尝试使用它会引发错误。

MatSort 已在 AfterViewInit 中准备就绪,但由于您需要在执行排序后将排序“应用”到数据源这一事实而变得更加复杂,这会通过“连接”到的渲染数据触发对视图的更改数据源。因此,您最终会遇到 ExpressionChangedAfterItHasBeenCheckedError,因为视图初始化生命周期尚未完成,但您已经在尝试更改它。

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

我认为没有 setTimeout() 就没有办法解决这两个问题,所以在这种情况下,从 OnInit 还是 AfterViewInit 调用它并不重要。

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

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

this.dataSource.data = myData;
Run Code Online (Sandbox Code Playgroud)

这使您不必在之后将呈现的数据连接到数据源,因此您可以在初始化数据源时执行此操作:

dataSource: MatTableDataSource<any> = new MatTableDataSource();
renderedData: Observable<any> = this.dataSource.connect();
Run Code Online (Sandbox Code Playgroud)


Emm*_*mmy 5

实际上有一种方法可以在不使用 setTimeout 的情况下做到这一点。这适用于 Angular 9,我还没有在以前的版本上测试过它,所以我不确定它是否适用于那里。

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

而不是放置:

@ViewChild(MatSort) sort: MatSort;
Run Code Online (Sandbox Code Playgroud)

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

    @ViewChild(MatSort) set matSort(sort: MatSort) {
        this.dataSource.sort = sort;
    }
Run Code Online (Sandbox Code Playgroud)

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