0

I'm trying to use the Angular-Material paginator. In the html, the pager appears after the list it applies to (where the user expects it).

Here is what it looks like (Data is just an array property from the component. That works fine. This is just about *ngFor depending on the paginator)

<mat-list *ngIf="Data">
  <mat-list-item *ngFor="let item of Data.slice(
                        dataPager.pageIndex * dataPager.pageSize, 
                        (dataPager.pageIndex + 1) * dataPager.pageSize
  )">
    {{item.name}}
  </mat-list-item>
  <mat-paginator #dataPager [length]="Data.length"
                            [pageSize]="10"
                            [pageSizeOptions]="[5, 10, 25, 50, 100]">
  </mat-paginator>
</mat-list>

However this gives me a big scary error

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngForOf: '. Current value: 'ngForOf: [object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]'.

Strictly speaking, everything seems to work fine anyways but I don't want to get this error at all. I don't get this error if a put the paginator before the *ngFor, but than the paginator isn't where I want it on the page. So how do I make sure the paginator component gets initialized before the *ngFor component that is based on it, even though the paginator is further down the page?

Tezra
  • 8,463
  • 3
  • 31
  • 68
  • It is not a good idea to call a function in your template `Data.slice(dataPager.pageIndex * dataPager.pageSize, (dataPager.pageIndex + 1) * dataPager.pageSize`. Store it in a member attribute. For example `*ngFor="let item of slicedData"` – Axiome Oct 02 '19 at 15:46
  • @Exomus What do you mean by member attribute? Do you mean a property in the component? – Tezra Oct 02 '19 at 15:52
  • Declare in your ts file a `slicedData = Data.slice(dataPager.pageIndex * dataPager.pageSize,(dataPager.pageIndex + 1) * dataPager.pageSize)` and call it in your template. That will avoid that everytime your template is regenerated, Angular calls the function. EDIT : You need to place wisely the `this` in the TS – Axiome Oct 02 '19 at 15:55
  • @Exomus I just came across the [slice pipe](https://angular.io/api/common/SlicePipe). Is the pipe any better or is it basically the same as calling slice? – Tezra Oct 07 '19 at 17:45
  • The slice pipe is an `impure` pipe. It means that it is called every cycle of the change detection. It's different from a pure pipe that is called only on reference change (A pure pipe is not triggered when there is deep mutations in a array for example). This pipe is based on the `Array.prototype.slice()` of Javascript API. So in a first approximation, it is the same. (but more elegant way). **Edit:** Be aware of impure pipe that can ruin user experience by using a lot of ressources – Axiome Oct 08 '19 at 13:39

2 Answers2

0

I don't think you can base your mat-list-item loop length on the dataPager values itself. You probably need to use the events it fires to re-calculate the list-view contents. You can initialize both with the same starting page size.

https://material.angular.io/components/paginator/api#PageEvent

rplst8
  • 1
  • 1
0

I ended up adjusting the conditions, and moving the pagniantor just outside the list, so that the paginator is created with the initial page, and then the ngFor doesn't run until the feeding Data is initialized in the ngAfterViewInit(). At least it prevents the initial page error. And I changed the paginator to default length to 0 if Data isn't initialized so that it doesn't have to wait for data.

<mat-list *ngIf="Data">
  <mat-list-item *ngFor="let item of Data.slice(
                        dataPager.pageIndex * dataPager.pageSize, 
                        (dataPager.pageIndex + 1) * dataPager.pageSize
  )">
    {{item.name}}
  </mat-list-item>
</mat-list>
<mat-paginator #dataPager [length]="(Data)? Data.length : 0"
                          [pageSize]="10"
                          [pageSizeOptions]="[5, 10, 25, 50, 100]">
</mat-paginator>
Tezra
  • 8,463
  • 3
  • 31
  • 68
  • Note that `*ngIf` can break MatPaginator according to this post : https://stackoverflow.com/questions/56028775/mat-paginator-breaks-when-mat-table-is-inside-of-ngif Refering to your question code, prefer [hidden]. Here your MatPaginator is outside of the ngIf condition so it is just fine – Axiome Oct 08 '19 at 13:57