ananya
ananya

Reputation: 1041

How to customize mat-paginator in Angular Material

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?

Upvotes: 24

Views: 58880

Answers (6)

Marshal
Marshal

Reputation: 11081

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

Upvotes: 12

AzizStark
AzizStark

Reputation: 1504

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

Upvotes: 5

Umair Hamid
Umair Hamid

Reputation: 3557

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

Upvotes: 9

Borja
Borja

Reputation: 3610

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

Upvotes: 2

pegaltier
pegaltier

Reputation: 559

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

Upvotes: 0

Wahrenheit Sucher
Wahrenheit Sucher

Reputation: 357

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>

Upvotes: 0

Related Questions