MatSort 和 MatPaginator 在没有 setTimeOut 的情况下不起作用 [英] MatSort and MatPaginator does not work without setTimeOut

查看:51
本文介绍了MatSort 和 MatPaginator 在没有 setTimeOut 的情况下不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我也试过:

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

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

<div *ngFor="让渲染数据记录 | async" matSort>//... 一些 html 来显示记录"<mat-paginator #paginator[pageSizeOptions]="[5, 10, 20]"></mat-paginator>

组件

导出类 ResultsComponent 实现 OnInit、OnDestroy、AfterViewInit {数据源:MatTableDataSource<任何>= 新 MatTableDataSource();renderData: Observable;@ViewChild(MatPaginator) 分页器:MatPaginator;@ViewChild(MatSort) 排序:MatSort;构造函数(某些服务){}ngOnInit() {const accountId = someOtherService.getAccountId();this.someService.getData(accountId).subscribe((myData) => {this.dataSource = new MatTableDataSource(myData);//如果没有包裹在 timeout 中,它将无法正常工作setTimeout(() => {this.dataSource.paginator = this.paginator;this.sort.sort(({id: 'created_on', start: 'desc'}));this.dataSource.sort = this.sort;});this.renderedData = this.dataSource.connect();}});}ngAfterViewInit() {}ngOnDestroy(): 无效 {if (this.dataSource) { this.dataSource.disconnect();}}}

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

解决方案

这里有几个生命周期计时问题在起作用,仔细想想,这是对的.

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

MatSort 已在 AfterViewInit 中准备就绪,但由于您需要在执行排序后将排序应用"到数据源,这会触发对视图的更改,即连接到数据源.因此,您最终会遇到 ExpressionChangedAfterItHasBeenCheckedError,因为视图初始化生命周期尚未完成,但您已经在尝试更改它.

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

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

对您的代码的一些其他观察:

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

this.dataSource.data = myData;

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

dataSource: MatTableDataSource= 新 MatTableDataSource();renderData: Observable= this.dataSource.connect();

I have a data coming from an endpoint and put it into MatTableDataSource. I was able to get it working for MatSort and MatPaginator, but needed to use setTimeOut, which does not seems to be a proper way to do this. If I remove this, it will complain that 'Can not read property of sort undefined' which I assumed this is due to it's not initialized yet.

I also have tried:

  • to move moving it to afterviewinit, but the data was loaded after afterviewinit getting called so it still does not work
  • using this.changeDetectorRef.detectChanges() after this.datasource = new ... also does not work

This is my current code (which is working, but using 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>

The Component

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(); }
    }
}

The above code is working for me, I'm just looking the right way not to use settimeout if possible.

解决方案

There are a couple of lifecycle timing issues at play here, and when you think about it, this is right.

The MatSort is part of the view, so it isn't 'ready' during OnInit - it is undefined. So trying to use it throws the error.

The MatSort is ready in AfterViewInit, but things are further complicated by the fact that you need to 'apply' the sort to the datasource after performing the sort, and this triggers changes to the view by way of the renderedData that is 'connected' to the datasource. You therefore end up with an ExpressionChangedAfterItHasBeenCheckedError because the view initialization lifecycle hasn't completed but you are already trying to change it.

So you can't sort until the view is ready, and you can't apply the sort when you are notified that the view is ready. The only thing you can do is wait until the end of the component initialization lifecycle. And you can do that using setTimeout().

I don't think there is any way around both of these problems without setTimeout(), so in that case it doesn't matter whether you call it from OnInit or AfterViewInit.

A couple of other observations on your code:

You don't need to create a new instance of MatTableDataSource in your subscription. You can assign the result data to the already created datasource:

this.dataSource.data = myData;

This frees you up from having to connect the rendered data to the datasource afterward, so you can do it when you initialize the datasource:

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

这篇关于MatSort 和 MatPaginator 在没有 setTimeOut 的情况下不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆