Angular MatPaginator doesn`t get initialized

AngularPaginationAngular Material

Angular Problem Overview


I have 2 components. Both have mat-table and paginators and pagination is working for one component and not working for the other component though the code is similar. Below is my html:

<div class="mat-elevation-z8">
    <mat-table [dataSource]="dataSource" matSort>
        <ng-container matColumnDef="col1">
            <mat-header-cell *matHeaderCellDef mat-sort-header> Column1 </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.col1}} </mat-cell>
        </ng-container>

        <ng-container matColumnDef="col2">
            <mat-header-cell *matHeaderCellDef mat-sort-header> Column2 </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.col2}} </mat-cell>
        </ng-container>

        <!-- Different columns goes here -->

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

    <mat-paginator #scheduledOrdersPaginator [pageSizeOptions]="[5, 10, 20]"></mat-paginator>
</div>

And below is my code in component.ts:

dataSource: MatTableDataSource<any>;
displayedColumns = ['col1', 'col2', ... ];

@ViewChild('scheduledOrdersPaginator') paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;

ngOnInit(): void {
    // Load data
    this.dataSource = new MatTableDataSource(somearray);
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
}

Similar code is working for the other component and table is getting rendered with the pagination properly, no clue what's wrong with this code.

Any help would be really appreciated

Angular Solutions


Solution 1 - Angular

In my case, the <mat-paginator> element was inside a container that had an *ngIf on it, which did not render until the data loaded asynchronously. This causes this.paginator to be undefined even in ngAfterViewInit. This causes it to silently fail as MatTableDataSource has no problem with you setting paginator to undefined.

The solution was to move the <mat-paginator> outside of the *ngIf'd container

Hopefully, this helps someone who is in the same situation as me.

Solution 2 - Angular

I resolved a similar issue by surrounding the instantiation with a timeout. Try this :

setTimeout(() => this.dataSource.paginator = this.paginator);

Solution 3 - Angular

Although selected answer works and solves the problem it is still a workaround. This is proper and more elegant way to deal with the problem.

Try adding AfterViewInit interface to your class, then put this.dataSource.paginator = this.paginator inside ngAfterViewInit() method

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

then you would not have to call workaround setTimeout.

Solution 4 - Angular

Angular 7+ (8/9/10/11/12) There is another elegant solution to solve that problem.

Short Version

Immediately after setting data source invoke ChangeDetectorRef.detectChanges().

Long Version

Step1:

Import ChangeDetectorRef & Material related stuff

import { ..., ChangeDetectorRef } from '@angular/core';
import { MatSort, MatTableDataSource, MatPaginator } from '@angular/material';

Step2:

Set class-properties in your component

@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;

Step3:

Inject ChangeDetectorRef in your constructor

constructor (..., private cdr: ChangeDetectorRef, ...) { ... }

Step4:

Set data source and invoke detectChanges()

this.dataSource = new MatTableDataSource (MY_DATA);
this.cdr.detectChanges();

Optional Steps:

After that, you can set other properties like

this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;

Solution 5 - Angular

1st Solution

Move mat-paginator from inside *ngIf div to outside

2nd Solution

use static false while declaring MatPaginator or MatSort

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

Solution 6 - Angular

This is because of this.paginator is undefined at the time it going to assign to this.dataSource.paginator.

if you using static data this will work

 @ViewChild(MatPaginator, {static: false}) paginator: MatPaginator; // For pagination
 @ViewChild(MatSort, {static: false}) sort: MatSort; // For Sort

 ngOnInit(): void {
   this.dataSource.data = this.dataList; // Data list is data array 
 }

 ngAfterViewInit(): void {
   this.dataSource.paginator = this.paginator; // For pagination
   this.dataSource.sort = this.sort; // For sort
 }

if you using dynamic data (Data from API) this will work

For Pagination

  @ViewChild(MatPaginator, {static: false})
  set paginator(value: MatPaginator) {
    if (this.dataSource){
      this.dataSource.paginator = value;
    }
  }

For Sort

  @ViewChild(MatSort, {static: false})
  set sort(value: MatSort) {
    if (this.dataSource){
      this.dataSource.sort = value;
    }
  }

As a side note im using Angular 9 at the movement.

Solution 7 - Angular

The question when the paginator is available in the view and can be retrieved and attached to the data source is the main crux of this issue and a common pitfall. Workarounds suggested here include using setTimeout() and ngAfterViewInit, are simply that - workarounds in the vein of "let's see how much we need to wait to make sure that the @ViewChild has set our component field with the correct paginator value".

The correct methodology is to attach the @ViewChild to a property setter and set the data source paginator as soon (and as often) as that setter is called with a valid paginator.

It is also very useful to have a single data source and not replace it every load (as I've seen many people do) - just bind a data source to the mat-table and update it's data field.

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

      <ng-container matColumnDef="col1">
        <mat-header-cell *matHeaderCellDef mat-sort-header> Column1 </mat-header-cell>
        <mat-cell *matCellDef="let row"> {{row.col1}} </mat-cell>
      </ng-container>

      <ng-container matColumnDef="col2">
        <mat-header-cell *matHeaderCellDef mat-sort-header> Column2 </mat-header-cell>
        <mat-cell *matCellDef="let row"> {{row.col2}} </mat-cell>
      </ng-container>

      <!-- additional columns go here -->

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

    <mat-paginator #scheduledOrdersPaginator [pageSizeOptions]="[5, 10, 20]"></mat-paginator>
  </div>
dataSource: MatTableDataSource<any> = new MatTableDataSource();
displayedColumns = ['col1', 'col2', ... ];

@ViewChild('scheduledOrdersPaginator') set paginator(pager:MatPaginator) {
  if (pager) this.dataSource.paginator = pager;
}

@ViewChild(MatSort) set sort(sorter:MatSort) {
  if (sorter) this.dataSource.sort = sorter;
}

ngOnInit(): void {
    this.loadData().subscribe(somearray => { this.dataSource.data = somearray; });
}

This approach should also solve the problem (noted by one of the commenters here) of having the paginator rendered late when hidden behind an *ngIf template - the paginator will be sent to the data source even if it gets rendered very late.

Solution 8 - Angular

To make it work I had to set the paginator after the data was fetched from source

getVariables() {
    this.activeRoute.params.subscribe(params => {
        if (params['id'] && (params['type'] === Type.CodeList)) {
            this.dataService
                .getItems(this.currentLanguage, params['id'])
                .subscribe((items: any) => {
                    this.dataSource.data = this.items;
                    this.dataSource.paginator = this.paginator;
                })
        }
    })
}

Solution 9 - Angular

It took me hours to figure it out.

The key is this.dataSource = new MatTableDataSource<MyInterface>(Object.values(data)); Then set this.dataSource.paginator = this.paginator;

I was using this.dataSource = data although I could get the data but the pagination was not working.

Again you have to use new MatTableDataSource

Works in Angular 11.

Solution 10 - Angular

The solution that I find is to to set the paginator after the data is successfully loaded.

this._empService.GetEmpList().subscribe(
  data => {
    this.empListDataSource = new MatTableDataSource<empToShow>(Object.values(data))
    this.empListDataSource.paginator = this.paginator
  }
)

Solution 11 - Angular

After trying all the above-provided solutions finally the simple solution worked for me. I am posting for reference if anyone stuck.

I am using Angular 8. Just add { static: false } in child reference

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

Got Solution From Here

I was using this :

@ViewChild(MatSort, { read: true, static: false }) sort: MatSort;

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

Solution 12 - Angular

None of the above solution worked for me.

I figured out what was the issue, mainly this.paginator will be undefined until the table is loaded and in view, that's why in some of the in places setTimeout solution works.

But in my case, my table was hidden behind some ngIf logic so the the table was only loaded after the ngIf becomes true(which happens on user interaction ) , but I was setting this.dataSource.paginator = this.paginator on ngOnInit

So, the solution depends on your case, the ground truth is make sure the table is loaded only then this.dataSource.paginator = this.paginator

I resolved it by, when user does the interaction and ngIf becomes true after that I call a function to set the paginator

     initPaginator(){
        this.dataSource.paginator = this.paginator
}

Solution 13 - Angular

What worked for me was to do implement what Lonely suggested, regarding ChangeDetectorRef, and also, set an object with static: false, in the @ViewChild, like this:

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

Solution 14 - Angular

I am quite a beginner in angular and typescript, but after having the same problem (except, that for me sorting didn't work either), what helped was creating a function 'refreshDataScource()' and having it called from ngAfterViewInit() as well as after each time the server responds with new data. In this function I simply refresh the dataSource with the paginator and the sort. Like this:

refreshDataSource() {
    this.dataSource = new MatTableDataSource(myDataArray);
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }

It fixed the paginator and the sort. Everything works now perfectly. However I am not sure if it is just a workaround or a real fix.

Solution 15 - Angular

Using setTimeOut() will solve the issue temporarily, however, this will again fail to work if you are pushing a huge load of Data [say 1000 rows] into MatDataSource.

we've found that the MatTable loads very slowly if a large data set is set before you set the data-source paginator.

ngOninit(){
// initialize dataSource here
}
    ngAfterViewInit() {
  this.dataSource.sort = this.sort;
  this.dataSource.paginator = this.paginator;

  /* now it's okay to set large data source... */
  this.dataSource.data = [GetLargeDataSet];}

So, initialize the data-Source at first place and set the properties like "Paginator" or "Sort" before pushing the data into the source into the "Data" property.

Solution 16 - Angular

@ViewChild(MatPaginator, {static: false}) paginator: any // For pagination
@ViewChild(MatSort, {static: false}) sort: any; // For Sort

use like this it will resolve the issue.

Solution 17 - Angular

  <mat-paginator
        #scheduledOrdersPaginator
          (page)="pageEvent($event)">
        </mat-paginator>
         pageEvent(event){
         //it will run everytime you change paginator
           this.dataSource.paginator = this.paginator;
          }

Solution 18 - Angular

Only change

dataSource: MatTableDataSource<any>;

for

dataSource = new MatTableDataSource();
dataSource = new MatTableDataSource();
displayedColumns = ['col1', 'col2', ... ];
@ViewChild('scheduledOrdersPaginator') paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
ngOnInit(): void {
    // Load data
    this.dataSource = new MatTableDataSource(somearray);
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
}

Solution 19 - Angular

I had the same issue (table data displaying but MatPaginator is not working). In my case, I forgot to create "new MatTableDataSource"

this.dataSource = somearray;

not enable MatPaginator while this.dataSource = new MatTableDataSource(somearray); does.

extract from material documentation

"To simplify the use case of having a table that can sort, paginate, and filter an array of data, the Angular Material library comes with a MatTableDataSource that has already implemented the logic of determining what rows should be rendered according to the current table state."

hope this answer helps someone.

Solution 20 - Angular

To paginate the table's data, add a <mat-paginator> after the table.

If you are using the MatTableDataSource for your table's data source, simply provide the MatPaginator to your data source. It will automatically listen for page changes made by the user and send the right paged data to the table.

Otherwise if you are implementing the logic to paginate your data, you will want to listen to the paginator's (page) output and pass the right slice of data to your table.

For more information on using and configuring the <mat-paginator>, check out the mat-paginator docs.

The MatPaginator is one provided solution to paginating your table's data, but it is not the only option. In fact, the table can work with any custom pagination UI or strategy since the MatTable and its interface is not tied to any one specific implementation.

@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
ngOnInit() {
  this.dataSource.paginator = this.paginator;
}

See also https://material.angular.io/components/table/overview

Solution 21 - Angular

Using setTimeout() is only a viable solution when you know how much time it will take to load the table. My problem was I using an *ngIf on the table (with !isLoading):

this.dataSource = new MatTableDataSource(this.rawData);

this.initPaginator();

this.isLoading = false;

The fix was setting my isLoading variable to false only after change detections and initializing the paginator:

this.dataSource = new MatTableDataSource(this.rawData);

this.isLoading = false;

this.cdr.detectChanges();

this.initPaginator();

So it loads the data -> shows the table -> detects changes -> inits paginator. I hope this helps anyone!

Solution 22 - Angular

for Angular version below 7, use read argument for MatPaginator.

@ViewChild(MatPaginator, {read: true}) paginator: MatPaginator;

this worked for me.

Note that this works for Dynamically loaded table data.

Solution 23 - Angular

  private paginator: MatPaginator;
  private sort: MatSort;

  @ViewChild(MatSort) set matSort(ms: MatSort) {
   this.sort = ms;
   this.setDataSourceAttributes();
  }

  @ViewChild(MatPaginator) set matPaginator(mp: MatPaginator) {
   this.paginator = mp;
   this.setDataSourceAttributes();
  }

  setDataSourceAttributes() {
   if(this.dataSource !== undefined){
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
   }
  }

Solution 24 - Angular

I had a similar issue to this, with mat-paginator inside a container with a ngIf.

The only thing that worked for me was the comment:

> Thank you - this was the solution for me. I opted to use [hidden] instead of ngIf so that the paginator would render even if there wasn't any data, but wouldn't show up to the user – TabsNotSpaces

To clarify, what I did was create a div, outside the container, with [hidden]=<negation_of_the_same_condition_as_the_ngIf>.

Solution 25 - Angular

Using async-await worked for me inside ngOnInit(), the paginator and sort has to wait!

   @ViewChild(MatPaginator) paginator: MatPaginator;
   @ViewChild(MatSort) sort: MatSort; 
    .
    .
    .
   ngOnInit() {
    this.isLoading = true;
    this._statsService.getAllCampgrounds().subscribe(
      async (response) => {
        this.allCampgrounds = response.allCampgrounds;
        this.dataSource = await new MatTableDataSource(this.allCampgrounds);
        
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;

        this.isLoading = false;
      },
      (error) => {
        this.isLoading = false;
        console.log(error);
      }
    );
  }

Solution 26 - Angular

None of the anwsers posted above helped me in this case.

The reason why my pagination didn't work depsite correct implementation was import

e.x.

import {MatPaginator} from "@angular/material/paginator";

didn't work, so I changed this component import to

import { MatTableDataSource, MatPaginator, MatSort } from '@angular/material';

Solution 27 - Angular

I was having similar issue, As I had two mat tables with mat paginators and only one of them works. I tried all of the above options, then I realized I was updating the datasource object instead of datasource.data, didn't realized that I was updating the type, thanks to @Bigeyes for sharing his answer.

Table loading data but paginator not working:

this.datasource = data.filter(x => (x.planName == this.planName && x.Termed_y == 1))

Table loading data and paginator working:

this.dataSource2.data = data.filter(x => (x.planName == this.planName && x.Termed_y == 1))

Solution 28 - Angular

A solution without using setTimeout is to use set :


 @ViewChild(MatPaginator) paginator: MatPaginator;


 set matPaginator(mp: MatPaginator) {
      this.paginator = mp;
      this.dataSource.paginator = this.paginator;
  }

Solution 29 - Angular

In my case, data coming from service asynchronously so neither could use ngOnInit or ngAfterViewInit, I used ngOnChanges, something like below:

ngOnChanges(change: SimpleChanges) {
    if (change.properties) {
      if (this.properties.length > 0) {
        this.dataSource = new MatTableDataSource(this.properties);
        this.dataSource.paginator = this.paginator;
      }
    }
  }

Be sure to set the [DataSource] attribute at mat-table element at html to the data source property from the component so to ensure the data source get bind with the table and paginator.

Solution 30 - Angular

This took me hours to finally track down and understand why my table wasn't working. Placing some console.logs() helped me figure out the order of events and why it didn't work consistently. My scenario was similar to the original issue above with a dynamic data source, but slightly different. For me, when I would first refresh my angular app, the paginator state would be set and then the data for my table would be set. When these events would happen in the this order the paginator worked just as expected.

Because I was using ReplaySubjects for getting the data for my tables, my paginator state would be set in ngAfterViewInit and then the table data would come in from my subscription (it depends on user ID so I don't have an initial value and that is why I didn't use BehaviorSubjects). My problem was that, when I would navigate to another component in my app, and return to my dynamic table data source, the table data would be set before the paginator state. This would make the paginator show page x, but the displayed data would always be the first page of data.

To fix this annoying issue I:

  1. Wrote a simple function for setting my table data as someone mentioned above:
  setTableData() {
    // set table data
    this.tableData.data = this.images.filter(img => this.filterData(img));

    // some other code below
    ....
  }
  1. Added two flags in my component, one for if I had loaded the table data and one for if my pagination state had been set. This way I could ensure that the pagination state is set before the data.
  // initialize our data source for our table and set flags for state
  tableData = new MatTableDataSource<IImage>(this.images);
  loading = true;
  setPageState = false;
  1. Added a setTimeout to ngAfterViewInit which would set my paginator state and set my table data only if the table data came through before ngAfterViewInit was called. The setTimeout prevents the annoying "expression changed after value was checked" error.
  ngAfterViewInit() {
    // add pagination state to our table
    setTimeout(() => {
      console.log('Set paginator state');
      this.setPageState = true;
      this.paginator.pageIndex = this.state.pageIndex;

      // check if we are not loading data, meaning the table came in first so
      // we need to set the data here
      if (!this.loading) {
        this.setTableData();
      }
    }, 0);
  }
  1. Lastly, in ngOnInit, where I subscribe to my data, I do not set my table data unless the paginator state was set first:
  ngOnInit() {
    console.log('ngOnInit');
    this.tableData.sort = this.sort;
    this.tableData.paginator = this.paginator;

    // listen for data
    this.dbService.images.subscribe(images => {
      console.log('Data subscription');
      // save images
      this.images = images;

      // only set table data if paginator state has been set, otherwise it doesnt work
      if (this.setPageState) {
        this.setTableData();
      }

      // hide spinner and update that we have data
      this.loading = false;
    });

    // other code
    .....
  }

And with that, my pagination was finally working consistently for when I first login to the app and when I navigate to other pages and go back to my dynamic table.

Solution 31 - Angular

I also had this problem. I added reference scheduledOrdersPaginator into pagintaor tag and received with @ViewChild. It solved my pagination problem. Below code may help others.

dataSource = new MatTableDataSource();
displayedColumns = ['col1', 'col2', ... ];
@ViewChild('scheduledOrdersPaginator') paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
ngOnInit(): void {
    // Load data
    this.dataSource = new MatTableDataSource(somearray);
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
}

html:

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

Solution 32 - Angular

Do check out this Stackoverflow Answer.

Setting the paginator in typescript after HTML components getting loaded (*ngIf) fixed the issue for me.

Solution 33 - Angular

Make sure that you have imported the MatPaginatorModule in the app.module.ts

Solution 34 - Angular

For me it was just add the property MatPaginator.length.

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

Solution 35 - Angular

I had a similar issue. Added the following to resolve it.

Step1
Add ChangeDetectorRef to constructor E.g

constructor(private cdr: ChangeDetectorRef) 

Step2
call detect change method and pagination in the init method

this.cdr.detectChanges(); 
this.dataSource.paginator = this.paginator;

Solution 36 - Angular

As the most voted answer mentions, it is due to a conflict caused by *ngIf:

SOLUTIONS:

  1. Leave the mat-paginator out of the *ngIf conditional.

  2. Change the *ngIf conditional to something else that suits your needs.

    2.1 You can replace it with [hidden]="conditional".

    2.2 You can also use [ngClass]="'d-none': conditional", and create a class that contains display: none.

There are others but personally the one I use when it causes this type of inconvenience is 2.2

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNagendra VarmaView Question on Stackoverflow
Solution 1 - AngularSkylar BrownView Answer on Stackoverflow
Solution 2 - Angularuser4676340View Answer on Stackoverflow
Solution 3 - AngularhopseyView Answer on Stackoverflow
Solution 4 - AngularLonelyView Answer on Stackoverflow
Solution 5 - AngularMuhammad BilalView Answer on Stackoverflow
Solution 6 - AngularCharitha Sampath GunawardanaView Answer on Stackoverflow
Solution 7 - AngularGussView Answer on Stackoverflow
Solution 8 - AngularHasseView Answer on Stackoverflow
Solution 9 - AngularBigeyesView Answer on Stackoverflow
Solution 10 - Angularningomba 2.0View Answer on Stackoverflow
Solution 11 - AngularShabbir IsmailView Answer on Stackoverflow
Solution 12 - AngularPranoy SarkarView Answer on Stackoverflow
Solution 13 - AngularWilhelm SorbanView Answer on Stackoverflow
Solution 14 - AngularGefilte FishView Answer on Stackoverflow
Solution 15 - AngularMadhavan NagarajanView Answer on Stackoverflow
Solution 16 - Angularuser16512189View Answer on Stackoverflow
Solution 17 - AngularMuhammad ShaheemView Answer on Stackoverflow
Solution 18 - AngularJoseanView Answer on Stackoverflow
Solution 19 - AngularPradeepal SudeshanaView Answer on Stackoverflow
Solution 20 - AngularMingqi ZhaoView Answer on Stackoverflow
Solution 21 - AngularJasperView Answer on Stackoverflow
Solution 22 - AngularDarshuuView Answer on Stackoverflow
Solution 23 - AngularMohammed ZubairView Answer on Stackoverflow
Solution 24 - Angularcorreia55View Answer on Stackoverflow
Solution 25 - AngularGauravView Answer on Stackoverflow
Solution 26 - AngularkbernatjanusziewiczView Answer on Stackoverflow
Solution 27 - AngularLokiView Answer on Stackoverflow
Solution 28 - AngularhzitounView Answer on Stackoverflow
Solution 29 - AngulardanferView Answer on Stackoverflow
Solution 30 - AngularZachView Answer on Stackoverflow
Solution 31 - AngularAmirView Answer on Stackoverflow
Solution 32 - Angularshaheer shukurView Answer on Stackoverflow
Solution 33 - AngularPhoenix404View Answer on Stackoverflow
Solution 34 - AngularLuis Antônio Costa FilhoView Answer on Stackoverflow
Solution 35 - AngularRameshView Answer on Stackoverflow
Solution 36 - AngularDavids Gonzalez TigreroView Answer on Stackoverflow