Angular Material Pagination Made Easy: Step-by-Step Guide for Navigation
Angular Material Pagination Made Easy: Step-by-Step Guide for Navigation

In this post you will learn how to implement pagination from Angular Material.

Finished project

Not so long ago I created a post about implementing custom pagination inside Angular. I still think that the custom way is a way to go because you are super flexible and pagination is not the most difficult component.

But some people wrote that they still prefer a library solution this is why in this video we will look how we can implement pagination from Angular Material.

Angular Material installation

Here I already prepared for us an empty Angular project and the first thing that we need to do is install Angular Material package.

npm install @angular/material

But it is not all. There is an additional dependency for Angular Material which is called Angular CDK.

npm install @angular/cdk

Configuring CSS

The next step is to configure our CSS from Angular Material.

Official paginator

As you can see on the official website paginator has nice CSS with items per page dropdown, pages information and buttons to switch the page. Everything here has nice CSS and transitions that we can get out of the box.

But in order to use it we need to install a theme of Angular Material in our project. In order to do that we must all theme CSS in angular.json.

"styles": [
  "@angular/material/prebuilt-themes/indigo-pink.css",
  "src/styles.css"
],

This will import prebuilt theme from Angular Material in our app.

Fonts & Icons

The last configuration that we need are fonts and icons from Angular Material.

// src/index.html
<html lang="en">
  <head>
    ...
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />
  </head>
  <body class="mat-typography">
    <app-root></app-root>
  </body>
</html>

We must add both imports to our index.html. And actually they are suitable for all components and not just for pagination. We also added a class mat-typography to our body.

Setting up pagination

In our to use pagination we must do some imports.

import { MatPaginatorModule } from '@angular/material/paginator';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  ...
  imports: [
    ...
    MatPaginatorModule,
    BrowserAnimationsModule,
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Here we need to import not only MatPaginatorModule which is needed for our pagination but also BrowserAnimationsModule as it uses some Angular animations inside.

Now we can render our pagination.

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

Here are all options that we need. length is a total amount of entities inside database. Paginator needs this information to calculate the amount of pages. pageSize is our limit of elements per page. showFirstLastButtons means that we want to see first and last buttons. pageSizeOptions are options for the dropdown where we can select how many items we need to show. pageIndex is an index of activated page and page output will be triggered when we select other page.

export class AppComponent {
  currentPage = 0;

  handlePageEvent(pageEvent: PageEvent) {
    console.log('handlePageEvent', pageEvent);
    this.currentPage = pageEvent.pageIndex;
  }
}

Here we defined our currentPage and handlePageEvent which is triggered every time when we jump to another page.

First iteration

As you can see our pagination is already rendered and is working just fine.

Overriding default behaviour

But we have a problem. As you can see on the screen instead of page information we see offset information which doesn't make any sense for user. This is what what I want to do is override this test to render meaningful information inside.

In order to do that we must extend and override service from paginator.

// src/app/paginatorIntl.service.ts
import {Injectable} from "@angular/core";
import {MatPaginatorIntl} from "@angular/material/paginator";

@Injectable()
export class PaginatorIntl extends MatPaginatorIntl {
  override getRangeLabel = (page: number, pageSize: number, length: number) => {
    return `Page ${page + 1} of ${Math.ceil(length/pageSize)}`
  }
}

Here we extended it from MatPaginatorIntl and overrode getRangeLabel. This is exactly what we see in pages information.

Now we just need to inject it correctly.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [{ provide: MatPaginatorIntl, useClass: PaginatorIntl }],
})
export class AppComponent {}

Here we provided our class inside the component as a MatPaginatorIntl provider. This means that instead of default service it will use our implementation.

Finished project

As you can see in browser it looks much better and the text is meaningful.

And actually if you want to improve your Angular knowledge and prepare for the interview I highly recommend you to check my course Angular Interview Questions - Coding Interview 2023.

📚 Source code of what we've done