How to customize mat-paginator in Angular Material
Asked Answered
D

6

23

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?

Delicate answered 6/12, 2018 at 7:11 Comment(0)
S
12

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

Silverpoint answered 6/12, 2018 at 20:35 Comment(16)
Okay. Thank you for your help @SilverpointDelicate
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. ThanksHonig
The stackblitz example isn't changing page when clicking on page number. Please update it.Harm
The numbers are not efffecting anything on the tableHarm
stackblitz.com/edit/material-angular-paginator-customized Here having the table attached with the pagination. See if you can fix this paginations of numbers.Harm
@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.Silverpoint
@Silverpoint welcome. Can you elaborate as to what changes you have made. Also the numbers are still not workable on clickingHarm
@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.Silverpoint
Sure @Silverpoint that will be really helpful. Thank you for your time and effort :)Harm
Hi @MrKhan, I believe I have it all sorted and the revised stackblitz link is available for review.Silverpoint
Its perfect @Marshal. Thanks a lot!Harm
HI @Silverpoint , This is very helpful, Thank you, Is it possible to show Total number of pages after ... buttons instead of firstlast buttons ?Telegenic
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?Telegenic
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.Silverpoint
@Silverpoint 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)Checkrow
This answer would be much better if the code were available here on Stack Overflow instead of on a separate site.Dusky
K
9

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

Koestler answered 15/1, 2020 at 10:59 Comment(1)
Life saver. Works great!Buffoon
T
5

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

Turgescent answered 24/9, 2020 at 15:52 Comment(4)
@Silverpoint 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. github.com/AzizStark/angular-custom-material-paginator/blob/…Turgescent
Will this line in your code also loop if there are 3000 action items? github.com/AzizStark/angular-custom-material-paginator/blob/…Silverpoint
No, that just iterates over the html elements. I fixed that for loop issue in this line. github.com/AzizStark/angular-custom-material-paginator/blob/…Turgescent
This answer would be much better if the code were available here on Stack Overflow instead of on a separate site.Dusky
A
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

Arv answered 30/8, 2019 at 11:50 Comment(0)
C
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>
Capita answered 29/1, 2019 at 20:38 Comment(0)
C
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://mcmap.net/q/585887/-mat-paginator-change-the-placing-of-mat-paginator-range-label

Colchicine answered 6/5, 2019 at 9:48 Comment(1)
This answer would be much better if the code were available here on Stack Overflow instead of on a separate site.Dusky

© 2022 - 2024 — McMap. All rights reserved.