21

I want to customize mat-paginator. By default I am getting paginator like this which have given in below link https://material.angular.io/components/paginator/overview. But I want pagination like below image. How can I do this using mat-paginator?

example showing paginator with round elevated buttons for the pages

Can anyone please help me with this?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
ananya
  • 1,001
  • 6
  • 33
  • 50

6 Answers6

11

Using @uhamid answer as inspiration, along with several comments below, indicating this is in fact possible, I revised my first attempt to provide a complete solution.

Although Umair Hamid example below is functional, it did not include the styling required. It also leveraged ngDoCheck which introduces recursive type behavior and is likely to introduce performance issues.

I also refactored most of the logic to make for a more complete solution.

Use it like:

<mat-paginator style-paginator showFirstLastButtons 
              [showTotalPages]="3"
              [length]="7130"
              [pageSize]="10"
              [pageSizeOptions]="[5, 10, 25, 100]">
</mat-paginator>

Provide page buttons to display as input [showTotalPages], if not provided it will default to 2


enter image description here


STACKBLITZ

https://stackblitz.com/edit/angular-8holwx?file=src/app/style-paginator.directive.ts

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Marshal
  • 10,499
  • 2
  • 34
  • 53
  • Okay. Thank you for your help @Marshal – ananya Dec 11 '18 at 05:17
  • Thanks for this solution. I tried it but when I click on page number it doesn't change page. Also, if I click on Next & Prev button then it works fine. Please update solution accordingly. Thanks – Ankur Raiyani Aug 01 '20 at 15:11
  • The stackblitz example isn't changing page when clicking on page number. Please update it. – Mr Khan Aug 09 '20 at 15:49
  • The numbers are not efffecting anything on the table – Mr Khan Aug 20 '20 at 10:18
  • https://stackblitz.com/edit/material-angular-paginator-customized Here having the table attached with the pagination. See if you can fix this paginations of numbers. – Mr Khan Aug 20 '20 at 10:57
  • @MrKhan and Ankur thank you for bringing this to my attention. I was able to replicate the issue and have provided a revised stackblitz above. It is not the most elegant code, but it should be close to a working example... please let me know if you identify any additional issues. – Marshal Aug 21 '20 at 03:48
  • @Marshal welcome. Can you elaborate as to what changes you have made. Also the numbers are still not workable on clicking – Mr Khan Aug 21 '20 at 04:28
  • 2
    @MrKhan I just fixed the numbers again, i broke them working on the button rendering. I need to revise the algorithm that calculates the range, the rendering of the buttons is still very glitchy, I will get this posted some time tomorrow. I will attempt a list of changes, but so far it has been a significant rewrite. You can compare the old stackblitz to the new for comparison. – Marshal Aug 21 '20 at 05:44
  • Sure @Marshal that will be really helpful. Thank you for your time and effort :) – Mr Khan Aug 21 '20 at 05:46
  • 1
    Hi @MrKhan, I believe I have it all sorted and the revised stackblitz link is available for review. – Marshal Aug 21 '20 at 19:19
  • Its perfect @Marshal. Thanks a lot! – Mr Khan Aug 23 '20 at 09:56
  • HI @Marshal , This is very helpful, Thank you, Is it possible to show Total number of pages after ... buttons instead of firstlast buttons ? – Soumya Gangamwar Dec 10 '20 at 07:45
  • At a time I want to 5 buttons , So If I pass [showTotalPages]="5" after pagegap event it only showing 4 buttons and after 2 times even it skipping 2 buttons,, How to fix this? – Soumya Gangamwar Dec 14 '20 at 06:56
  • I designed it that way so that the active button is always in the middle. I could featurize that so it can be disabled, it would likely be later in the week before I can refactor, will update answer when available. – Marshal Dec 14 '20 at 14:03
  • @Marshal this solution is perfect. But I got some issues here. When pageSize is 5, I'm selecting pageIndex 4 (page5) When I change to page size 10, the pageIndex is 2 (page 3). how I can config it when I change the pageSize , the pageIndex always return to 0 (the first page) – TLVF2627 Oct 07 '21 at 14:37
  • 1
    This answer would be much better if the code were available here on Stack Overflow instead of on a separate site. – Heretic Monkey Mar 08 '23 at 17:32
8

With the help of @Marshal, I have created a directive for pagination with dot gap.

Copy this code in your directive

import {
  AfterViewInit,
  Directive,
  DoCheck,
  Host,
  Optional,
  Renderer2,
  Self,
  ViewContainerRef,
} from '@angular/core';
import { MatPaginator } from '@angular/material';

@Directive({
  selector: '[appStylePaginator]',
})
export class StylePaginatorDirective implements AfterViewInit, DoCheck {
  public currentPage = 1;
  public directiveLoaded = false;
  public pageGapTxt = '...';
  constructor(
    @Host() @Self() @Optional() private readonly matPag: MatPaginator,
    private readonly vr: ViewContainerRef,
    private readonly ren: Renderer2,
  ) {}

  private buildPageNumbers(pageCount, pageRange) {
    let dots = false;
    const paglast = pageCount;
    const pagcurrent = this.matPag.pageIndex;
    const showTotalPages = 8;
    for (let i = 0; i < paglast; i = i + 1) {
      if (
        i === pagcurrent ||
        (pagcurrent < showTotalPages && i < showTotalPages) ||
        (i > pagcurrent - (showTotalPages - 1) && i < pagcurrent) ||
        i > paglast - 1 ||
        (i > pagcurrent && i < pagcurrent + showTotalPages)
      ) {
        this.ren.insertBefore(pageRange, this.createPage(i, this.matPag.pageIndex), null);
      } else {
        if (i > pagcurrent && !dots) {
          this.ren.insertBefore(pageRange, this.createPage(this.pageGapTxt, this.matPag.pageIndex), null);
          dots = true;
        }
      }
    }
  }

  private createPage(i: any, pageIndex: number): any {
    const linkBtn = this.ren.createElement('mat-button');
    this.ren.addClass(linkBtn, 'mat-icon-button');

    const pagingTxt = isNaN(i) ? this.pageGapTxt : +(i + 1);
    const text = this.ren.createText(pagingTxt + '');
    this.ren.addClass(linkBtn, 'mat-custom-page');

    switch (i) {
      case pageIndex:
        this.ren.setAttribute(linkBtn, 'disabled', 'disabled');
        break;
      case this.pageGapTxt:
        this.ren.setAttribute(linkBtn, 'disabled', 'disabled');
        break;
      default:
        this.ren.listen(linkBtn, 'click', () => {
          this.currentPage = i;
          this.switchPage(i);
        });
        break;
    }

    this.ren.appendChild(linkBtn, text);
    return linkBtn;
  }

  private initPageRange(): void {
    const pagingContainerMain = this.vr.element.nativeElement.querySelector('.mat-paginator-range-actions');

    if (
      this.vr.element.nativeElement.querySelector('div.mat-paginator-range-actions div.btn_custom-paging-container')
    ) {
      this.ren.removeChild(
        pagingContainerMain,
        this.vr.element.nativeElement.querySelector('div.mat-paginator-range-actions div.btn_custom-paging-container'),
      );
    }

    const pagingContainerBtns = this.ren.createElement('div');
    const refNode = this.vr.element.nativeElement.childNodes[0].childNodes[0].childNodes[2].childNodes[5];
    this.ren.addClass(pagingContainerBtns, 'btn_custom-paging-container');
    this.ren.insertBefore(pagingContainerMain, pagingContainerBtns, refNode);

    const pageRange = this.vr.element.nativeElement.querySelector(
      'div.mat-paginator-range-actions div.btn_custom-paging-container',
    );
    pageRange.innerHtml = '';
    const pageCount = this.pageCount(this.matPag.length, this.matPag.pageSize);
    this.buildPageNumbers(pageCount, pageRange);
  }

  private pageCount(length: number, pageSize: number): number {
    return Math.floor(length / pageSize) + 1;
  }

  private switchPage(i: number): void {
    this.matPag.pageIndex = i;
    this.matPag._changePageSize(this.matPag.pageSize);
  }

  public ngAfterViewInit() {
    setTimeout(() => {
      this.directiveLoaded = true;
    }, 500);
  }

  public ngDoCheck() {
    if (this.directiveLoaded) {
      this.initPageRange();
    }
  }
}

After that you just need to add this directive in our module's entryComponents.

Use it like:

<mat-paginator
  appStylePaginator //<<== Use of directive
  (page)="pageChangeEvent($event)"
  [length]="pageLength"
  [pageSize]="pageSize"
  showFirstLastButtons
>
</mat-paginator>

The output is now: enter image description here

Umair Hamid
  • 3,509
  • 3
  • 23
  • 25
4

I wanted a custom paginator with both forward and backward dots with event emitter. So, I improved the @marshal's directive by clearing many bugs and modified to make it work exactly like the ngx-pagination.

Just wanted to share in case if anyone needs this, the code is here Repo link

Custom Pagination

AzizStark
  • 1,390
  • 1
  • 17
  • 27
  • 1
    @Marshal There were too many. One major bug I noticed is the for loop which is inefficient. For example if the number of pages is 3000 for loop runs for 3000 times. I fixed that along with other bugs. I have also written a test file to ensure everything works correctly. https://github.com/AzizStark/angular-custom-material-paginator/blob/master/src/app/paginator.directive.spec.ts – AzizStark Nov 24 '20 at 12:39
  • Will this line in your code also loop if there are 3000 action items? https://github.com/AzizStark/angular-custom-material-paginator/blob/47f58322e6bfdb7aed7a13b43870cdde8ec4e31d/src/app/pagination.directive.ts#L109 – Marshal Nov 24 '20 at 14:12
  • No, that just iterates over the html elements. I fixed that for loop issue in this line. https://github.com/AzizStark/angular-custom-material-paginator/blob/47f58322e6bfdb7aed7a13b43870cdde8ec4e31d/src/app/pagination.directive.ts#L141 – AzizStark Nov 24 '20 at 17:31
  • This answer would be much better if the code were available here on Stack Overflow instead of on a separate site. – Heretic Monkey Mar 08 '23 at 17:32
2

Insert the buttons from mat-paginator I think it is not possible but you can create the custom pager:

paginator-configurable-example.html

  <button mat-button (click)="page.previousPage()"><</button>
  <button mat-button (click)="updateManualPage(1)" >1</button>
  <button mat-button (click)="updateManualPage(2)">2</button>
  <button mat-button (click)="updateManualPage(3)">3</button>
  <button mat-button (click)="page.nextPage()">></button>

  <mat-paginator style="visibility:hidden" [pageIndex]="pageIndex" #page [length]="100" [pageSize]="10" [pageSizeOptions]="[5, 10, 25, 100]" (page)="pageEvent = $event" ></mat-paginator>

<div *ngIf="pageEvent">
  <h5>Page Change Event Properties</h5>
  <div>List length: {{pageEvent.length}}</div>
  <div>Page size: {{pageEvent.pageSize}}</div>
  <div>Page index: {{pageEvent.pageIndex}}</div>
</div>

paginator-configurable-example.ts

import {Component} from '@angular/core';
import {PageEvent} from '@angular/material/paginator';

/**
 * @title Configurable paginator
 */
@Component({
  selector: 'paginator-configurable-example',
  templateUrl: 'paginator-configurable-example.html',
  styleUrls: ['paginator-configurable-example.css'],
})
export class PaginatorConfigurableExample {
  // MatPaginator Inputs
  length = 100;
  pageSize = 10;
  pageSizeOptions: number[] = [5, 10, 25, 100];
  manualPage = null;

  // MatPaginator Output
  pageEvent: PageEvent;

  setPageSizeOptions(setPageSizeOptionsInput: string) {
    this.pageSizeOptions = setPageSizeOptionsInput.split(',').map(str => +str);
  }

  public updateManualPage(index: number): void {
    this.manualPage = index;
    this.pageEvent.pageIndex = index;
  }
  public clearManualPage(): void {
    this.manualPage = 0;
  }
}

example image

borchvm
  • 3,533
  • 16
  • 44
  • 45
0

Marshal is incorrect. You can set the pageIndex property of the material paginator https://material.angular.io/components/paginator/api#MatPaginator

I mimicked exactly what you were trying to do. Hard coded so you'll have to figure it out how to add buttons based on the number of pages but here you go.

<button mat-fab (click)="page.previousPage()"><</button>
          <button mat-fab (click)="page.pageIndex = 0">1</button>
          <button mat-fab (click)="page.pageIndex = 1">2</button>
          <button mat-fab (click)="page.pageIndex = 2">3</button>
          <button mat-fab (click)="page.nextPage()">></button>
          <mat-paginator style="visibility:hidden" #page [length]="100" [pageSize]="10" [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>
0

You can find here my custom directive (StylePaginatorDirective) inspired by the answer provided by @Marshal but completely rewriten from scratch in order to shows the pagination inside the mat-paginator-range-label

Preview

enter image description here

Stackblitz

https://stackblitz.com/edit/angular-wyx2ue-ayitwa?file=app%2Fstyle-paginator.directive.ts

https://angular-wyx2ue-ayitwa.stackblitz.io

Positioning

Feel free to customize the ordering of your component with custom css class: https://stackoverflow.com/a/55969038/2835268

pegaltier
  • 494
  • 4
  • 11