bitscanbyte
bitscanbyte

Reputation: 700

Initialising MatPaginator asynchronously

We initialise MatPaginator in our components like

@ViewChild(MatPaginator) paginator: MatPaginator;

and assign to a MatTable DataSource defined in our component as

matTableDataSource.paginator = this.paginator;

This doesn't work in a use case when the data source is initialised asynchronously using async pipe with a combination of *ngIf. As,

<div *ngIf="someDataObs | async as yourData else loading">
   <mat-table #table [dataSource]="yourData">
      ... your headers and columns
   </mat-table>
   <mat-paginator [pageSize]="10" showFirstLastButtons></mat-paginator>
</div>

Note: you could put mat-paginator below the div with *ngIf, but that's not an ideal solution, more so if you need to show multiple tables in a same component coming with a single backend asynchronous call.

Since the mat-paginator will get initialised asynchronously, using a code such as below will not work as paginator will be null.

ngAfterViewInit() {
  this.someDataObs = this.backendService.get()
    .map(value => {
      const matTableDataSource = new MatTableDataSource<SomeType>(value);
      matTableDataSource.paginator = this.paginator; // will be null here
      return matTableDataSource;
    });
}

To have a way for this to work would've required MatPaginator to have an output property to emit once initialised, but that doesn't exist for now.

Use of async pipe and *ngIf makes the code really concise and you should not avoid it to overcome this use case.

Any solution to achieve this would be helpful.

Upvotes: 3

Views: 2950

Answers (4)

Welyngton Dal Pra
Welyngton Dal Pra

Reputation: 774

There is another solution on GitHub:

Just add this HIDDEN before the table While HIDDEN==TRUE the table will not be rendered before the dataSource is loaded:

<div [hidden]="isLoading">
  <mat-table [dataSource]="dataSource">
  ...
  </mat-table>
</div>

Upvotes: 0

Welyngton Dal Pra
Welyngton Dal Pra

Reputation: 774

I tested this Github issue solution and it works fine

Just add this *ngIf="pagination" to your mat-paginator in HTML:

<table mat-table [dataSource]="dataSource">

  <!-- Position Column -->
  <ng-container matColumnDef="position">
    <th mat-header-cell *matHeaderCellDef> No. </th>
    <td mat-cell *matCellDef="let element"> {{element.position}} </td>
  </ng-container>
  ......

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

<mat-paginator [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons 
*ngIf="pagination" > <!-- THIS!!! -->
</mat-paginator>

And then add this estructure to your component

export class TableComponent implements OnInit {
@Input('pagination') pagination: boolean;
@ViewChild(MatPaginator,  {static: false}) set matPaginator(paginator: 
MatPaginator) {
  if (this.pagination) {
    this.dataSource.paginator = paginator;
  }
}
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
....
}

Upvotes: 0

HappyProgrammerUser
HappyProgrammerUser

Reputation: 81

I just had the same problem.This is a known problem and they have solved this, in my opinion, better.

 @ViewChild(MatPaginator) set matPaginator( paginator: MatPaginator){
   this.dataSource.paginator = paginator;
 }
 @ViewChild(MatSort) set matSort( sort: MatSort){
   this.dataSource.sort = sort;
 }

https://github.com/angular/material2/issues/10205#issuecomment-465455599

Upvotes: 1

Wandrille
Wandrille

Reputation: 6821

Endeed, your problem is the *ngIf.

With @ViewChild(MatPaginator) paginator: MatPaginator, when paginator is requested, it is not found in the DOM and it return null.

A solution to this problem, is to replace your *ngIf by a statement with display:none. So the paginator will stay in the DOM.

Upvotes: 0

Related Questions