'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;
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:
- 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
....
}
- 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;
- 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);
}
- 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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow