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
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:
setTableData() {
// set table data
this.tableData.data = this.images.filter(img => this.filterData(img));
// some other code below
....
}
// initialize our data source for our table and set flags for state
tableData = new MatTableDataSource<IImage>(this.images);
loading = true;
setPageState = false;
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);
}
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.
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>
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.
Angular 7+ (8/9) 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;
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.
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
}