'Angular MatPaginator doesn`t get initialized

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



Solution 1:[1]

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

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

Solution 2:[2]

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 3:[3]

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:[4]

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:[5]

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:[6]

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:[7]

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:[8]

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:[9]

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:[10]

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:[11]

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:[12]

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 13:[13]

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 14:[14]

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:[15]

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:[16]

@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:[17]

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

Solution 18:[18]

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:[19]

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 20:[20]

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:[21]

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 22:[22]

  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 23:[23]

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 24:[24]

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 25:[25]

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:[26]

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:[27]

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:[28]

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:[29]

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:[30]

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.