76

I'm new to angular and trying to implement pagination in my app. I am trying to use this material component.

With the code below, I can get length, pagesize, and pageSizeOptions in my .ts file

<md-paginator [length]="length"
              [pageSize]="pageSize"
              [pageSizeOptions]="pageSizeOptions"
</md-paginator>
export class PaginatorConfigurableExample {
  length = 100;
  pageSize = 10;
  pageSizeOptions = [5, 10, 25, 100];
}

but I can't seem to trigger a function to change the data on the table above when the page is changed. Also, how do I use the nextPage, previousPage, hasNextPage, and hasPreviousPage methods?

ssuperczynski
  • 3,190
  • 3
  • 44
  • 61
Drunken Daddy
  • 7,326
  • 14
  • 70
  • 104
  • check this [plunker](https://plnkr.co/edit/?p=preview) this might help . For pagination you might also use this lib i feel its much simpler [Link](import {NgxPaginationModule} from "ngx-pagination";).I have used it [here](https://rahulrsingh09.github.io/AngularConcepts/#/comment) – Rahul Singh Jul 26 '17 at 06:06
  • are you sure you posted the right plunker link? – Drunken Daddy Jul 26 '17 at 06:49
  • sorry, but still not the right plunker I think, it opens to https://plnkr.co/edit/?p=preview – Drunken Daddy Jul 26 '17 at 06:58
  • check this it wasnt saved it [plunker](https://plnkr.co/edit/Cd9d1pfIUQDimpu4H6VB?p=preview) – Rahul Singh Jul 26 '17 at 07:02
  • I am interested if there is a better solution now, for directly handling some events like `clicked nextPage`? – Max Aug 08 '18 at 16:42

11 Answers11

95

I'm struggling with the same here. But I can show you what I've got doing some research. Basically, you first start adding the page @Output event in the foo.template.ts:

 <md-paginator #paginator
                [length]="length"
                [pageIndex]="pageIndex"
                [pageSize]="pageSize"
                [pageSizeOptions]="[5, 10, 25, 100]"
                (page)="pageEvent = getServerData($event)"
                >
 </md-paginator>

And later, you have to add the pageEvent attribute in the foo.component.ts class and the others to handle paginator requirements:

pageEvent: PageEvent;
datasource: null;
pageIndex:number;
pageSize:number;
length:number;

And add the method that will fetch the server data:

ngOnInit() {
   getServerData(null) ...
}

public getServerData(event?:PageEvent){
  this.fooService.getdata(event).subscribe(
    response =>{
      if(response.error) {
        // handle error
      } else {
        this.datasource = response.data;
        this.pageIndex = response.pageIndex;
        this.pageSize = response.pageSize;
        this.length = response.length;
      }
    },
    error =>{
      // handle error
    }
  );
  return event;
}

So, basically, every time you click the paginator, you'll activate getServerData(..) method that will call foo.service.ts getting all data required. In this case, you do not need to handle nextPage and nextXXX events because they will be automatically calculated upon view rendering.

I hope this can help you. Let me know if you had success. =]

aelkz
  • 1,786
  • 1
  • 24
  • 26
37

Hey so I stumbled upon this and got it working, here is how:

inside my component.html

<mat-paginator #paginator [pageSize]="pageSize" [pageSizeOptions]="[5, 10, 20]" [showFirstLastButtons]="true" [length]="totalSize"
    [pageIndex]="currentPage" (page)="pageEvent = handlePage($event)">
</mat-paginator>

Inside my component.ts

public array: any;
public displayedColumns = ['', '', '', '', ''];
public dataSource: any;    

public pageSize = 10;
public currentPage = 0;
public totalSize = 0;

@ViewChild(MatPaginator) paginator: MatPaginator;

constructor(private app: AppService) { }

ngOnInit() {
  this.getArray();
}

public handlePage(e: any) {
  this.currentPage = e.pageIndex;
  this.pageSize = e.pageSize;
  this.iterator();
}

private getArray() {
  this.app.getDeliveries()
    .subscribe((response) => {
      this.dataSource = new MatTableDataSource<Element>(response);
      this.dataSource.paginator = this.paginator;
      this.array = response;
      this.totalSize = this.array.length;
      this.iterator();
    });
}

private iterator() {
  const end = (this.currentPage + 1) * this.pageSize;
  const start = this.currentPage * this.pageSize;
  const part = this.array.slice(start, end);
  this.dataSource = part;
}

All the paging work is done from within the iterator method. This method works out the skip and take and assigns that to the dataSource for the Material table.

Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
Wesley Coetzee
  • 4,768
  • 3
  • 27
  • 45
  • 5
    You need to declare your pageEvent as a property ( pageEvent: PageEvent; ) otherwise it will run only in dev mode (ng serve) but not in production (ng build configuration=production ). Production is more restrictive and compiler will complain you missing pageEvent properties on your component. – Steve S. Oct 07 '19 at 23:20
  • What if data is not retrieved (like 404, 400 and such http errors)? And in case its serverside paging: what about waiting for loading server data? If you would click on next page for x times and the data is not retrieved fast enough for the UI, the paginator may glitch or delayed showing the desired page data. – Michael Nov 19 '21 at 11:08
  • You'd have to use a `pipe()` to catch the error and handle that case – Wesley Coetzee Nov 23 '21 at 11:48
29

based on Wesley Coetzee's answer i wrote this. Hope it can help anyone googling this issue. I had bugs with swapping the paginator size in the middle of the list that's why i submit my answer:

Paginator html and list

<mat-paginator [length]="localNewspapers.length" pageSize=20
               (page)="getPaginatorData($event)" [pageSizeOptions]="[10, 20, 30]"
               showFirstLastButtons="false">
</mat-paginator>
<mat-list>
   <app-newspaper-pagi-item *ngFor="let paper of (localNewspapers | 
                         slice: lowValue : highValue)"
                         [newspaper]="paper"> 
  </app-newspaper-pagi-item>

Component logic

import {Component, Input, OnInit} from "@angular/core";
import {PageEvent} from "@angular/material";

@Component({
  selector: 'app-uniques-newspaper-list',
  templateUrl: './newspaper-uniques-list.component.html',
})
export class NewspaperUniquesListComponent implements OnInit {
  lowValue: number = 0;
  highValue: number = 20;

  // used to build a slice of papers relevant at any given time
  public getPaginatorData(event: PageEvent): PageEvent {
    this.lowValue = event.pageIndex * event.pageSize;
    this.highValue = this.lowValue + event.pageSize;
    return event;
  }

}
avokado
  • 441
  • 4
  • 10
17

Another way to link Angular Paginator with the data table using Slice Pipe.Here data is fetched only once from server.

View:

 <div class="col-md-3" *ngFor="let productObj of productListData | 
     slice: lowValue : highValue">
       //actual data dispaly  
 </div>

<mat-paginator [length]="productListData.length" [pageSize]="pageSize" 
   (page)="pageEvent = getPaginatorData($event)">
</mat-paginator> 

Component

    pageIndex:number = 0;
    pageSize:number = 50;
    lowValue:number = 0;
    highValue:number = 50;       

  getPaginatorData(event){
     console.log(event);
     if(event.pageIndex === this.pageIndex + 1){
        this.lowValue = this.lowValue + this.pageSize;
        this.highValue =  this.highValue + this.pageSize;
       }
    else if(event.pageIndex === this.pageIndex - 1){
       this.lowValue = this.lowValue - this.pageSize;
       this.highValue =  this.highValue - this.pageSize;
      }   
       this.pageIndex = event.pageIndex;
 }
Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
Asridh Kumar
  • 515
  • 1
  • 6
  • 19
  • Nice one, if you just want client side pagination. Helped me. Thanks @Asridh – Aarchie Oct 18 '18 at 17:40
  • This example doesn't fully answer the original question. The original question included pageSizeOptions, which this example ignores. For future readers, I handled this by including: this.highValue = this.highValue + event.pageSize - this.pageSize; this.pageSize = event.pageSize; Still giving the the original post an upvote because it's super quick/easy and works regardless of whether you're using MatTable, which many other answers assume. – FredM Jul 31 '19 at 16:38
6

Component:

import { Component,  AfterViewInit, ViewChild } from @angular/core;
import { MatPaginator } from @angular/material;

export class ClassName implements AfterViewInit {
    @ViewChild(MatPaginator) paginator: MatPaginator;
    length = 1000;
    pageSize = 10;
    pageSizeOptions: number[] = [5, 10, 25, 100];

    ngAfterViewInit() {
        this.paginator.page.subscribe(
           (event) => console.log(event)
    );
}

HTML

<mat-paginator 
   [length]="length"
   [pageSize]="pageSize" 
   [pageSizeOptions]="pageSizeOptions"
   [showFirstLastButtons]="true">
</mat-paginator>
Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
Viree
  • 317
  • 3
  • 3
5

The issue in the original question is that you are not capturing "page" event, to resolve this you need to add (page)='yourEventHandler($event)' as an attribute in the md-paginator tag.

check a working example here

You can also check the API docs here

Prashant Pimpale
  • 10,349
  • 9
  • 44
  • 84
LH7
  • 1,385
  • 2
  • 12
  • 16
2

After spending few hours on this i think this is best way to apply pagination. And more importantly it works. This is my paginator code <mat-paginator #paginatoR [length]="length" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions">

Inside my component @ViewChild(MatPaginator) paginator: MatPaginator; to view child and finally you have to bind paginator to table dataSource and this is how it is done ngAfterViewInit() {this.dataSource.paginator = this.paginator;} Easy right? if it works for you then mark this as answer.

Chetan Sharma
  • 161
  • 11
2

This issue is resolved after spending few hours and i got it working. which is believe is the simplest way to solve the pagination with angular material. - Do first start by working on (component.html) file

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

and do in the (component.ts) file

 import { MatPaginator } from '@angular/material/paginator';
 import { Component, OnInit, ViewChild } from '@angular/core';

 export interface UserData {
 full_name: string;
 email: string;
 mob_number: string;
 }

 export class UserManagementComponent implements OnInit{
  dataSource : MatTableDataSource<UserData>;

  @ViewChild(MatPaginator) paginator: MatPaginator;

  constructor(){
    this.userList();
   }

    ngOnInit() { }

   public userList() {

   this._userManagementService.userListing().subscribe(
      response => {

      console.log(response['results']);
      this.dataSource = new MatTableDataSource<UserData>(response['results']);
      this.dataSource.paginator = this.paginator;
      console.log(this.dataSource);

     },


      error => {});

     }

 }

Remember Must import the pagination module in your currently working module(module.ts) file.

  import {MatPaginatorModule} from '@angular/material/paginator';

   @NgModule({
      imports: [MatPaginatorModule]
    })

Hope it will Work for you.

2

The tricky part here is to handle is the page event. We can follow angular material documentation up to defining page event function.

Visit https://material.angular.io/components/paginator/examples for the reference.

I will add my work with hours of hard work in this regard. in the relevant HTML file should look like as follows,

<pre>
<mat-paginator
    [pageSizeOptions]="pageSizeOptions"
    [pageSize]="Size"
    (page)="pageEvent = pageNavigations($event)"
    [length]="recordCount">
</mat-paginator>
</pre>

Then go to the relevant typescript file and following code,

declarations,

@Input('data') customers: Observable<Customer[]>;
pageEvent: PageEvent;
Page: number=0;
Size: number=2;
recordCount: number;
pageSizeOptions: number[] = [2,3,4,5];

The key part of page navigation as follows,

pageNavigations(event? : PageEvent){
    console.log(event);
    this.Page = event.pageIndex;
    this.Size = event.pageSize;
    this.reloadData();
  }

We require the following function to call data from the backend.

reloadData() {

    this.customers = this.customerService.setPageSize(this.Page ,this.Size);
    this.customerService.getSize().subscribe(res => {
      this.recordCount = Number(res);
     
      console.log(this.recordCount);
    });
  }

Before the aforementioned implementation, our services file should contain the following service call

setPageSize(page: number, size: number): Observable<any> {
    return this.http.get(`${this.baseUrl}?pageSize=${size}&pageNo=${page}`);
  }

Then all set to go and enable pagination in our app. Feel free to ask related questions thank you.

0

THIS WORKS FOR ME:

Make sure that you define the datasource in the ngOnInit() (avoid doing it in the ngViewAfterInit function).. something like this

dataSource: MatTableDataSource<Listing> = new MatTableDataSource();
pageEvent: PageEvent;
datasource: null;
pageSizeOptions: number[] = [5, 10, 12, 15];
pageIndex: number;
pageSize: number;
length: number;

constructor(private yourService: YourService) {
}
ngOnInit() {
   this.dataSource.paginator = this.paginator;
   this.dataSource.sort = this.sort;
   this.pageIndex=0;
   this.pageSize=10;
   this.yourService.getListItems(this.pageSize, 
   this.pageIndex).subscribe(res => {
          this.dataSource.data = res;
          this.lenght=res.lenght();
       });
   }  
handlePageEvent(event: PageEvent) {
   this.yourService.getListItems(event.pageSize, 
   event.pageIndex).subscribe(e=>{
     this.dataSource.data = res;
});       
return event;
}

 

And your html component like this

 <mat-paginator (page)="handlePageEvent($event)"
      [pageSizeOptions]="pageSizeOptions"
      [pageSize]="pageSize"
      class="sticky left-0"
      [length]="length"
      [pageIndex]="pageIndex"
      aria-label="Select page"
  ></mat-paginator>
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Shoniisra
  • 621
  • 7
  • 6
0

I skimmed through the answers, and while there's some good implements, I think there are cleaner (more ng-esque) ways...

In the HTML, we want to add the #paginator as selector for use when binding, like @ViewChild(MatPaginator) paginator!: MatPaginator;

    <mat-paginator #paginator
        [pageSizeOptions]="[5, 10, 20]"
        showFirstLastButtons
        aria-label="Select page of periodic elements">
    </mat-paginator>

In the relative code, assign the paginator to this.dataSource.paginator, and subscribe to the Observable property named page.

// bind to paginator control
@ViewChild(MatPaginator) paginator!: MatPaginator;

// subscribe to paginator events via observable page property
ngAfterViewInit() {
  this.dataSource.paginator = this.paginator;
  this.paginator.page.subscribe(page => {
    this.searchItems();
  });
}

NOTE: searchItems makes use of the paginator to make request for the number so items and specific page, and when no args search is without filter.

Call your method to fetch items (from API or other source) and populate table table.

NOTE: In a real-world scenario we would want to be doing all this through state operations.

Code is at https://github.com/abcox/toybox-web-ng2/tree/main/src/app/contact/contact-list

Adam Cox
  • 3,341
  • 1
  • 36
  • 46