如何使用MatTableDataSource与observable?

42

我正在使用 mat-table,并尝试使用 MatTableDataSource 与可观察对象一起使用(从 Web 服务获取数据),但我不知道如何配置 MatTableDataSource 以使用可观察对象而不是数组。

解决此问题的唯一方法是在 ngOnInit 方法中订阅可观察对象,并始终在新数据到达时创建新的 MatTableDataSource 吗?

目前我的做法是这样的,但我不知道这是否是使用 MatTableDataSource 与可观察对象一起工作的正确解决方案。

dataSource: MatTableDataSource<Thing>;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;
    
ngOnInit() {
    getThings().subscribe(things => {
        this.dataSource = new MatTableDataSource(things);
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;
    });
}

1
根据 Angular Material 文档,我认为这是正确的解决方案。如果你想将数据存储到 dataSource 中,那么你必须使用新的 MatTableDataSource()。 - Tushar
请查看我在此帖子上的回答:https://dev59.com/R7Hma4cB1Zd3GeqPR9ov#69329180使用Observable与MatTableDataSource完全是可行的。 - Crash1hd
6个回答

48

你应该能够在类级别上新建MatTableDataSource,然后在ngOnInit中使用data设置器。

dataSource = new MatTableDataSource<Thing>();
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;

ngOnInit() {
    getThings().subscribe(things => {
        this.dataSource.data = things;
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;
    });
}

2
请注意,在设置数据后进行排序会慢得多:https://dev59.com/C1UL5IYBdhLWcg3wfoDS#51296374(在我的应用程序中,这种情况没有链接中那么极端,但仍然产生了明显的差异。) - DennisK
1
是的,我建议将订阅放在 ngAfterViewInit 函数下面。 :D - Shan Surat
3
如果我不想在组件中订阅它,而是在模板中使用异步来获取数据怎么办? - Jeremy Scott Peters
设置分页器和排序必须在ngAfterViewInit中完成。 - JiBi
使用Observable的问题的答案怎么样? - Mr. Gung

35

使用MatTableDataSource作为Observable

您可以通过pipe操作符将您的Observable进行处理:

thingsAsMatTableDataSource$: Observable<MatTableDataSource<Thing>>  =
  getThings().pipe(
    map(things => {
      const dataSource = new MatTableDataSource<Thing>();
      dataSource.data = things;
      return dataSource;
}));

您可以在模板中对可观察对象使用异步管道:

[dataSource]="thingsAsMatTableDataSource$ | async"

这样您就不需要订阅,仍然可以享受 mat-table 的排序等功能...

避免重复的构造函数调用

只需将其实例化为私有成员,并使用该实例即可:

private dataSource = new MatTableDataSource<Thing>();

thingsAsMatTableDataSource$: Observable<MatTableDataSource<Thing>>  =
  getThings().pipe(
    map(things => {
      const dataSource = this.dataSource;
      dataSource.data = things
      return dataSource;
}));

这是一个在 Stackblitz 上的简单例子。


4
这似乎是最整洁、最安全、最符合惯用语的方法,我可以确认它是可行的。我很惊讶它只有如此少的赞。 - bighairdave
6
谢谢,它可以工作,但无法使用Observable <MatTableDataSource <Thing>>,可以使用Observable <any>,错误如下:类型'MatTableDataSource<Service> | null'无法分配给类型'CdkTableDataSourceInput<Service>'。 - Puneet Sharma
1
我试图使用这个解决方案,但在HTML文件中出现以下错误:类型'MatTableDataSource<Thing> | null'无法分配给类型'CdkTableDataSourceInput<Thing>'。你知道原因吗? - levi
1
和@PuneetSharma和levi一样的问题...更新这个答案会很好。 - Lewis Cianci
你可以通过返回一个默认的空源或者将其类型设置为 MatTableDataSource<Thing> | null 来确保始终返回预期的对象。但在第二种情况下,你的模板应该能够处理空值。 - H3AR7B3A7
显示剩余2条评论

6

这是一种解决方法,因为MatTableDataSource不支持将Observable作为数据源。

import { MatTableDataSource } from '@angular/material';
import { Observable, Subscription } from 'rxjs';
import { SomeInterface} from './some.interface';

export class CustomDataSource extends MatTableDataSource<SomeInterface> {

    private collection: SomeInterface[] = [];

    private collection$: Subscription;

    constructor(collection: Observable<SomeInterface[]>) {
        super();
        this.collection$ = collection.subscribe(data => {
           this.data = data; // here you have to adjust the behavior as needed
        });
    }

   disconnect() {
     this.collection$.unsubscribe();
     super.disconnect();
   }
}

然后在组件中:

dataSource: CustomDataSource;

ngOnInit(): void {
  const observableData$ = getData();
  this.dataSource = new CustomDataSource(observableData$);
  // this.dataSource.sort = this.sort; // add sorting or filter
}

例子:StackBlitz


你能给你的代码添加一些解释吗? - HDJEMAI
我在我的帖子中做了一些更改,并在Stackblitz上添加了示例。 - burasuk

2
我已经发布了一个相关库:@matheo/datasource 在这篇文章中,我解释了一些基本概念:
https://medium.com/@matheo/reactive-datasource-for-angular-1d869b0155f6 在示例代码中,您可以看到我如何从Firebase数据库获取项目并在示例存储库中操作分页、排序和过滤。希望您喜欢它并给我您的意见 ;)

你在 Stackblitz 上的示例已经无法工作了。 - Frimlik

2
你也可以使用observable,只需(*)。最初的回答
[dataSource]="dataSource|async"

(*) 实际上您不需要使用管道异步

请参见stackblitz中的示例,我将文档的第一个示例替换为:

dataSource = of(ELEMENT_DATA).pipe(delay(1000));

1
但是使用排序和分页功能并不那么容易。 - Christoph Hummler
抱歉,你没问题。dataSource可以是一个MatTableDataSource、数组或可观察对象,但只有在它是MatTableDataSource时才能对其进行排序。 - Eliseo

0

<mat-table [dataSource]="(things$ | async) ?? []">

<mat-table [dataSource]="(things$ | async) ?? []">

  things$: Observable<MatTableDataSource<Thing>> =
    this.thingService.getThings().pipe(map(things => new MatTableDataSource<Thing>(things))); ``` 

with this approach async pipe and MatTableDataSource works without error. Sort and pagination can be used afterwards.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接