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.