Mat Paginator Not Working Inside ngIf


Mat Paginator Not Working Inside ngIf. viewChid is undefined.

In the previous tutorial, you saw how to use pagination in Angular using Mat Paginator. Mat Paginator stops working when inside ngIf.

Here is our existing app.component.html file.

<div class="container">
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

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

    <ng-container matColumnDef="username">
      <th mat-header-cell *matHeaderCellDef> UserName </th>
      <td mat-cell *matCellDef="let element"> {{element.username}} </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="website">
      <th mat-header-cell *matHeaderCellDef> Website </th>
      <td mat-cell *matCellDef="let element"> {{element.website}} </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
    <tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>
  </table>
  <mat-paginator #paginator [pageSize]="2" [pageSizeOptions]="[2, 5, 10, 25, 100]">
  </mat-paginator>

</div>

Let’s say you add an ngIf to conditionally show the Mat table.

<div *ngIf="showData" class="container">
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

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

    <ng-container matColumnDef="username">
      <th mat-header-cell *matHeaderCellDef> UserName </th>
      <td mat-cell *matCellDef="let element"> {{element.username}} </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="website">
      <th mat-header-cell *matHeaderCellDef> Website </th>
      <td mat-cell *matCellDef="let element"> {{element.website}} </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
    <tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>
  </table>
  <mat-paginator #paginator [pageSize]="2" [pageSizeOptions]="[2, 5, 10, 25, 100]">
  </mat-paginator>

</div>

I added a variable called showData to conditionally show/hide mat table.

import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { DataService } from './data.service';
import { ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  dataSource: any = [];

  showData : boolean = false;

  @ViewChild('paginator') paginator!: MatPaginator;

  columnsToDisplay = ['name', 'username', 'email', 'website'];
  
  constructor(private service: DataService) {}

  ngOnInit() {
    this.service.getUserData().subscribe((response : any) => {
      if(response && response.length){
        this.showData = true;
        console.log('paginator is ', this.paginator);
        this.dataSource = new MatTableDataSource(response);
        this.dataSource.paginator = this.paginator;
      } else{
        this.showData = false;
      }
    })
  }
}

Save the above changes and refresh the Angular app. The pagination will not work. If you check the console logged value of paginator, it will be undefined.

I tried checking the value of paginator in the ngAfterViewInit also. But there also it’s undefined.

So, the solution is to trigger the change detection using ChangeDetectorRef. Import it to app.component.ts and create an instance of it in constructor.

import { ChangeDetectorRef } from '@angular/core';

constructor(private service: DataService, private ref: ChangeDetectorRef) {}

Once the API returns response you can trigger the change detection by triggering the detectChanges method. That should set the value of paginator.

Here is the modified app.component.ts.

import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { DataService } from './data.service';
import { ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  dataSource: any = [];

  showData : boolean = false;

  @ViewChild('paginator') paginator!: MatPaginator;

  columnsToDisplay = ['name', 'username', 'email', 'website'];
  
  constructor(private service: DataService, private ref: ChangeDetectorRef) {}

  ngOnInit() {
    this.service.getUserData().subscribe((response : any) => {
      if(response && response.length){
        this.showData = true;
        this.dataSource = new MatTableDataSource(response);
        this.ref.detectChanges();
        this.dataSource.paginator = this.paginator;
      } else{
        this.showData = false;
      }
    })
  }
}

Save the above changes and the mat paginator should be working fine.

Source code from this tutorial is available on GitHub.