Angular Material MatPaginator showing page 0 of 0

8.8k Views Asked by At

sorry for the question in advance. I googled my problem before writing this and tried lots of solutions, but without luck. I'm an Angular noob trying to learn Material. My simple project makes a http request and uses the JSON received to populate a table. The Angular Material table is working fine, but the paginator is not. It is greyed and shows page 0 of 0.

The component.html is as follows:

<div style="height: 80vh;">
  <mat-toolbar color="primary">
    <mat-toolbar-row>
      <button mat-icon-button (click)="sidenav.toggle()" fxShow="true" fxHide.gt-sm>
        <mat-icon>menu</mat-icon>
      </button>
      <span>Teste - Requisição Http/Tabela</span>
      <span class="menu-spacer"></span>
    </mat-toolbar-row>
  </mat-toolbar>

  <mat-sidenav-container fxFlexFill>
    <mat-sidenav #sidenav>
      <mat-nav-list>
        <a (click)="sidenav.toggle()" mat-list-item>Fechar</a>
        <a href="#" mat-list-item>Link 1</a>
        <a href="#" mat-list-item>Link 2</a>
        <a href="#" mat-list-item>Link 3</a>
      </mat-nav-list>
    </mat-sidenav>
    <mat-sidenav-content fxFlexFill>
      <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

        <ng-container matColumnDef="id">
          <th mat-header-cell *matHeaderCellDef> Id </th>
          <td mat-cell *matCellDef="let element"> {{element.id}} </td>
        </ng-container>

        <ng-container matColumnDef="email">
          <th mat-header-cell *matHeaderCellDef> Email </th>
          <td mat-cell *matCellDef="let element"> {{element.email}} </td>
        </ng-container>

        <ng-container matColumnDef="first_name">
          <th mat-header-cell *matHeaderCellDef> First Name </th>
          <td mat-cell *matCellDef="let element"> {{element.first_name}} </td>
        </ng-container>

        <ng-container matColumnDef="last_name">
          <th mat-header-cell *matHeaderCellDef> Last Name </th>
          <td mat-cell *matCellDef="let element"> {{element.last_name}} </td>
        </ng-container>

        <ng-container matColumnDef="avatar">
          <th mat-header-cell *matHeaderCellDef> Avatar </th>
          <td mat-cell *matCellDef="let element"> {{element.avatar}} </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, 25, 100]" showFirstLastButtons></mat-paginator>
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

The component.ts is as follows:

import { HttpClient } from '@angular/common/http';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';

export interface UserData {
  id: number;
  email: string;
  first_name: string;
  last_name: string;
  avatar: string;
}

const USER_DATA: UserData[] = [];

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
  title = 'Test';
  url = 'https://reqres.in/api/users?page=1';
  displayedColumns: string[] = ['id', 'email', 'first_name', 'last_name', 'avatar'];
  dataSource = new MatTableDataSource<UserData>(USER_DATA);
 
  constructor(private http: HttpClient) {
  
    this.http.get(this.url).toPromise().then(data => {
      console.log(data["data"]);
      this.dataSource = data["data"]
      console.log(this.dataSource)
    });
  }

  @ViewChild(MatPaginator) paginator: MatPaginator;

  ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
  }
}

What am I doing wrong? Thanks

4

There are 4 best solutions below

3
On BEST ANSWER

Try adding #paginator in

<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>

It should be:

<mat-paginator #paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>

Without the id "#paginator" the @ViewChild decorator will never be able to find the element (this id must be the same as the used in the @ViewChild decorator line). This little detail is not included in the Pagination example in the Angular Material documentation (or I was not able to see it..)

Beside this, try declaring the dataSource object as:

dataSource! = MatTableDataSource<UserData>;

instead of

dataSource = new MatTableDataSource<UserData>(USER_DATA);

Then, when the promise is resolved, assign the contents this way

this.dataSource = new MatTableDataSource<UserData>(data.data);

instead of

this.dataSource = data["data"]

Immediately after that, assign the paginator component to the dataSource.paginator:

this.dataSource.paginator = this.paginator;

6
On

In the angular's documentation, they forgot to add, that you need to define { static: false} or {static: true} .

Like this:

@ViewChild(MatPaginator, {static:false}) paginator: MatPaginator;

in docs:

static - True to resolve query results before change detection runs, false to resolve after change detection. Defaults to false.

You need to set it to false, if you want to use default.

3
On

Try removing the AfterView and replace the ViewChild with the below code:

@ViewChild(MatPaginator) set matPaginator(paginator: MatPaginator) {
    this.dataSource.paginator = paginator;
}
0
On

I needed to add this.paginator.length = +data.meta.total; so that length prop of paginator knew the total possible items, and could calculate the pagination.

Adding, the example from ng docs doesn't show this and docs show as 'hard coded' values.

ng docs need work!