Components: Table: Paginator/Sorting for dynamic data does not work (Bug)

Created on 11 Nov 2017  路  13Comments  路  Source: angular/components

Bug, feature request, or proposal:

Currently it is not possible to use the paginator with dynamic data that comes from Subjects (or initially Observables). The paginator seems to work with __data_ (initialData) set in the MatTableDataSource constructor. You can provide an Array that then transformed into a BehaviorSubject; however passing in a BehaviourSubject directly (e.g. from a service that provides the dynamic data) should be a thing - that would fix the issue described above.

What is the expected behavior?

The table should refresh when switching pages via the paginator.

What is the current behavior?

The table does not refresh when switching pages via the paginator.

What are the steps to reproduce?

Unfortunately I can't provide a functional Plunker because the data I'm using comes from my backend app... But you can find the relevant parts here: http://plnkr.co/edit/exXUHnTlOiEipaH97m4n

Basically you setup a regular MatTable and assign a DataSource. Then you return the BehaviourSubject in your DataSource's connect() method. This makes the data appear in the table - but you won't be able to sort or paginate.

What is the use-case or motivation for changing an existing behavior?

Pagination and sorting should also work with dynamic data. ;)

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Material 2

Is there anything else we should know?

It's my third week with Angular. ^^

Most helpful comment

I don't believe this change is necessary.

  1. When using a custom data source implementation, you can do whatever you want and pass any observable streams or static data you need into the data source.
  2. When using MatTableDataSource, you are not confined to the initial data and can use the data property to update it. Pagination and sorting will still work fine.
// create data source
this.myDataSource = new MatTableDataSource(initialData);

// update data in data source when available
this.streamOfDataUpdates.subscribe(newData => this.myDataSource.data = newData);

All 13 comments

I don't believe this change is necessary.

  1. When using a custom data source implementation, you can do whatever you want and pass any observable streams or static data you need into the data source.
  2. When using MatTableDataSource, you are not confined to the initial data and can use the data property to update it. Pagination and sorting will still work fine.
// create data source
this.myDataSource = new MatTableDataSource(initialData);

// update data in data source when available
this.streamOfDataUpdates.subscribe(newData => this.myDataSource.data = newData);

True, that works... I didn't think about that, even though I did a similar thing to fill my data list... ^^

How would you subscribe to an array of objects to detect changes? binding mat table to an array and using push to add a new item does not update table data source. So I am newing up the data source object:

selectedProcedures is a mat data source object.

const data= this.selectedProcedures.data;
data.push(result);

// force change detection of mat-table by newing up the data source
this.selectedProcedures.data = data;

The data object would still have the same reference. Try this:

// force change detection of mat-table by newing up the data source
this.selectedProcedures.data = [...this.selectedProcedures.data, result];

I tried both above ways even datasource is data is changed, but still we don't find mat-table working and adding the new row

It might help if you do the following after loading the data.

this.paginator.page // your paginator instance
      .pipe(
        tap(() => data) // your dynamic data
      );

The data object would still have the same reference. Try this:

// force change detection of mat-table by newing up the data source
this.selectedProcedures.data = [...this.selectedProcedures.data, result];

is this work if I go to the next page and further come to the previous page? I don't think it will work.

I don't believe this change is necessary.

  1. When using a custom data source implementation, you can do whatever you want and pass any observable streams or static data you need into the data source.
  2. When using MatTableDataSource, you are not confined to the initial data and can use the data property to update it. Pagination and sorting will still work fine.
// create data source
this.myDataSource = new MatTableDataSource(initialData);

// update data in data source when available
this.streamOfDataUpdates.subscribe(newData => this.myDataSource.data = newData);

This works for me like magic. Thanks

@willshowell i'm stuck with use pagination with BehaviorSubject

i was used BehaviorSubject for update table value.

Init var:
zoneCategoryItems: BehaviorSubject<IZoneCategoryInterface[]> = new BehaviorSubject([]);
*Service call for getting data from server * :

this._masterService.getMasterZoneCategoryData().subscribe((Category: IZoneCategoryInterface[]) => {
           this.zoneCategoryItems.next(Category);
    }, () => {
      this.zoneCategoryItems.next(null);
    })

if you want to update just used

this.zoneCategoryItems.next([...this.zoneCategoryItems.getValue(), result]);

But i face one ISSUE i can't use MatPaginator with BehaviorSubject anyone some idea?
if i want to use MatPagination it must be zoneCategoryItems =new MatTableDataSource<IZoneCategoryInterface>(); ?

Not to bad with a bit of massaging

export class TopicTableDataSource extends DataSource<TopicTableItem> {
  private sort: MatSort;
  private dataSubject = new BehaviorSubject<TopicTableItem[]>([]);

  constructor(private readonly topicsService: TopicsService) {
    super();
    this.topicsService.findAllTopics().pipe(
      catchError(() => of([])),
    ).subscribe(data => this.dataSubject.next(data));
  }

  public setSort(sort: MatSort) {
    this.sort = sort;
    this.sort.sortChange.subscribe(() => this.dataSubject.next(this.getSortedData(this.dataSubject.getValue())))
  }
  connect(): Observable<TopicTableItem[]> {
    return this.dataSubject.asObservable();
  }

  constructor(private readonly topicsService: TopicsService) {
  }

  ngOnInit() {
    this.dataSource = new TopicTableDataSource(this.topicsService);
  }

  ngAfterViewInit() {
    this.dataSource.setSort(this.sort);
    this.table.dataSource = this.dataSource;
  }
}

@ricardosaracino Thanks so much :)

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings