DEV Community

Cover image for Adding multiple MatPaginators to the same DataSource
Robin
Robin

Posted on • Edited on

Adding multiple MatPaginators to the same DataSource

This short guide is for all Angular developers using the Angular Material Library. A customer requested the following feature:

Please show the (Mat)Paginator above and below all tables (which are MatTables).

The problem here: You can only connect a single MatPaginator to a DataSource that holds the data for the table.

First I tried to use templates to display the paginator twice on the same page, but without success. The second paginator didn't work at all.

Second, I thought about implementing the pagination logic by myself, as you do it with server-side pagination. As I had to edit several pages, this did not seem to be the best way to do it.

One step before the final implementation, I also experimented with using different signals to synchronise the properties to the second paginator instance. The final solution is a bit simpler.

Implementation

Template:

<!-- first paginator (standard implementation with some additional options) --> 
<mat-paginator
  pageSize="50"
  [pageSizeOptions]="[10, 25, 50, 100]"
  [showFirstLastButtons]="false">
</mat-paginator>

<!-- MatTable as usual --> 
<table mat-table [dataSource]="dataSource">...</table

<!-- second paginator (synced explicit to the dataSource) -->
@if (dataSource.paginator; as paginator) {
  <mat-paginator
    (page)="pageChanged($event)"
    [pageSize]="paginator.pageSize"
    [length]="paginator.length"
    [pageIndex]="paginator.pageIndex"
    [pageSizeOptions]="paginator.pageSizeOptions"
    [showFirstLastButtons]="paginator.showFirstLastButtons">
  </mat-paginator>
}
Enter fullscreen mode Exit fullscreen mode

Component:

@Component(/* ... */)
export class DocumentListComponent implements AfterViewInit {
  // input of the data
  documents = input.required<Document[]>();

  // dataSource of the table
  dataSource = new MatTableDataSource<Document>();

  // finds the first MatPaginator instance in the view
  @ViewChild(MatPaginator, { static: true })
  paginator!: MatPaginator;

  // listening to changes in the documents input
  constructor() {
    effect(() => (this.dataSource.data = this.documents()));
  }

  // connecting the first paginator to the dataSource
  ngAfterViewInit(): void {
    this.dataSource.paginator = this.paginator;
  }

  // handling page changes from the bottom paginator
  pageChanged(event: PageEvent) {
    const dataSourcePaginator = this.dataSource.paginator;

    if (dataSourcePaginator) {
      dataSourcePaginator.pageIndex = event.pageIndex;
      dataSourcePaginator._changePageSize(event.pageSize);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation

How does it work? Implement the MatTable and the first MatPaginator as usual, connect them in the ngAfterViewInit() hook.

The second paginator will not be updated automatically, so we need to pass in all the required properties that we can retrieve from the first paginator, which also controls the displayed data in the table.

Finally we also need to handle the navigation events of the bottom paginator. Therefore we added a pageChanged() method, that updates the state of the first paginator and the dataSource along with it.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay