Server Side Pagination in Angular mat-table using mat-paginator
In previous article we learnt about adding client side pagination in Angular mat-table
using mat-paginator
.
In client side pagination, we will get all records from the server at a time and apply pagination using mat-paginator
component.
But If we huge data i.e., more number of records it’s not a good idea to apply the client side pagination.
It might lead to serious performance impacts.
In that case we will implement server side pagination in Angular mat-table.
What is Server Side Pagination in Angular?
Let’s say we have to display 1000 records in mat-table
Angular component.
In client side pagination, we will get all 1000 records from the server and bind it to the mat-table
data source and further using mat-paginator
component we will add pagination.
Getting 1000 records from the server using HttpClient.get()
might be slow.
The server has to read all records from the database and return it to the client in JSON format.
The JSON data size will be more.
May be 1000 records is not a big deal, imagine loading around 1,00,000 records?
Clearly there will be a performance impact.
In mat-table
pagination by default we will see only first page records, we can define the page size and accordingly number of pages will increase.
By clicking next page or page number we will see the remaining records.
So at any given point of time we will see only fixed number of records in the table. i.e., pageSize
.
Let’s say our page size is “10”.
Instead of getting 1000 records, we will get only first 10 records from the server on initial load and display them in the UI.
And if we click on second page, we will again call server to get next 10 records.
So each time when we click the next page or page number we will go to server get corresponding page records.
This is called Server Side Pagination.
And server API should accept page size and page number parameters.
This is the minimum requirement to implement server side pagination in Angular.
Data from the server should have following data format.
Server API : "/api/users?currentPage=1&pageSize=6"
// returns
{
data: [{},{}]
currentPage: 1
pageSize: 6
totalPages: 2
totalRecords: 12
}
The data
attribute contains the records to display in the mat-table
.
currentPage
is nothing but page number.
Remaining attributes pageSize
, totalPages
and totalRecords
are self explanatory.
It’s not necessary that your JSON data should be in above format, but those attributes are necessary and will be helpful while displaying the table records.
And it’s widely accepted format for server side pagination.
Steps to Implementing Server Side Pagination in Angular.
Step 1 : Add mat-paginator
to the mat-table
First we will add pagination to the material table using mat-paginator
, as explained in previous articles.
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container [matColumnDef]="column" *ngFor="let column of displayedColumns">
<th mat-header-cell *matHeaderCellDef>{{ column }}</th>
<td mat-cell *matCellDef="let emp">{{ emp[column] }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let emprow; columns: displayedColumns"></tr>
</table>
<mat-paginator
#paginator
[pageSizeOptions]="pageSizes"
showFirstLastButtons></mat-paginator>
In component.ts file
dataSource = new MatTableDataSource<Employee>();
@ViewChild('paginator') paginator: MatPaginator;
pageSizes = [3, 5, 7];
In this example we will show the list of employees in the mat-table
.
export interface Employee {
id: number;
first_name: string;
last_name: string;
email: string;
avatar: string;
}
Step 2: Add length
attribute to the mat-paginator
mat-paginator
has an attribute called length
, which represents the total number of records in the material table or length of data to be displayed.
I have created a variable called totalData
and bind it to the length
attribute.
<mat-paginator
#paginator
[length]="totalData"
[pageSizeOptions]="pageSizes"
showFirstLastButtons
></mat-paginator>
Step 3: Create Server API which supports page number & page size parameter
As explained above Our serer API should accept page number and page length parameters in order to implement server side pagination.
I am using a third party API https://reqres.in
, which returns list of employee details and also accepts two parameters called page
and per_page
.
https://reqres.in/api/users?page=1&per_page=5
Here page
is nothing but page number and per_page
represents page size.
Step 4: Create a Service
Now we will create a service called EmployeeService
with the method called getEmployees()
.
Inside this method, using HttpClient.get()
method we will call the API.
export class EmployeeService {
constructor(private http: HttpClient) {}
public getEmployees(
pageNumber: Number,
pageSize: Number
): Observable<EmployeeTable> {
const url = `https://reqres.in/api/users?page=${pageNumber}&per_page=${pageSize}`;
return this.http.get<EmployeeTable>(url);
}
}
In the above code I have used EmployeeTable
object instead of Employee
, this is because our server API returns the data in following format.
export interface EmployeeTable {
data: Employee[];
page: number;
per_page: number;
total: number;
total_pages: number;
}
Inject this service in our component.ts file.
constructor(public empService: EmployeeService) {}
getTableData$(pageNumber: Number, pageSize: Number) {
return this.empService.getEmployees(pageNumber, pageSize);
}
I have added a local method in the component.ts file which calls the getEmployees()
observable in the EmployeeService
.
Step 5: Subscribe to mat-paginator
page event
mat-paginator
contains a property called page
which is an EventEmitter
of type PageEvent
.
And will be triggered when the paginator changes the page size or page index (i.e., page number).
In ngAfterViewInit()
method, I am subscribing to this.paginator.page
event.
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.paginator.page
.pipe(
startWith({}),
switchMap(() => {
return this.getTableData$(
this.paginator.pageIndex + 1,
this.paginator.pageSize
).pipe(catchError(() => observableOf(null)));
}),
map((empData) => {
if (empData == null) return [];
this.totalData = empData.total;
return empData.data;
})
)
.subscribe((empData) => {
this.EmpData = empData;
this.dataSource = new MatTableDataSource(this.EmpData);
});
}
I am using series of Rxjs Operators like startWith
and switchMap
and map
along with PageEvent of paginator.
In the switchMap
operator I am returning getTableData$
observable with page number and page size parameters.
mat-paginator
contains pageIndex
and pageSize
attributes which represents page number and page size of mat-table
.
As pageIndex
start with zero, I am adding +1
to the page index.
In the map
operator I am assigning total number of records to the totalData
variable and returning employee records from the data
attribute of EmployeeTable
.
Finally in the subscribe method, assigning the employee data to mat-table
data source.
Whenever there is a change in mat-paginator
, i.e., when we click on previous/next page links or when we change the page size the above page event called and the table data will be updated accordingly.
Adding progress bar to the mat-paginator.
As we are calling the server API on each mat-paginator
change event, it’s better to add progress bar to the mat-table
.
If the server call takes too much time then there will be an indication to the user that the data being loaded.
I am using mat-progress-bar
to represent the loading of table records.
<h2>mat-table server side pagination</h2>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container [matColumnDef]="column" *ngFor="let column of displayedColumns">
<th mat-header-cell *matHeaderCellDef>{{ column }}</th>
<td mat-cell *matCellDef="let emp">{{ emp[column] }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let emprow; columns: displayedColumns"></tr>
</table>
<mat-progress-bar mode="indeterminate" *ngIf="isLoading"></mat-progress-bar>
<mat-paginator
#paginator
[length]="totalData"
[pageSizeOptions]="pageSizes"
showFirstLastButtons
></mat-paginator>
And using isLoading
variable showing the progress bar on top of mat-paginator
.
mat-table server side pagination example StackBlitz Demo
Here is the demo for mat-table pagination examplehttps://stackblitz.com/edit/angular-mat-table-server-side-pagination-example