Back

Server-side Pagination in Angular

Server-side Pagination in Angular

Web apps implement pagination to cut large files and let them be viewed through several pages. In [Angular] (https://angular.dev/) applications, the functionality can be conducted either on the client’s side or the server’s side. This article discusses server-side pagination and its benefits over client-side pagination, using the ngx-pagination library.

Server-side pagination fetches only a chunk of data from the server for each page view, while client-side pagination returns all the data from the server at once before dividing it into pages on the client side. When working with a huge dataset, server-side pagination becomes necessary since it guarantees that relatively little data is processed and sent to the client.

It also reduces the client’s memory usage, thereby enhancing the app’s performance. It only contains the information needed to render a specific page instead of requiring all the contents to be downloaded each time.

Difference Between Client-Side and Server-Side Pagination

Here are some differences between server-side and client-side pagination.

AspectServer-Side PaginationClient-Side Pagination
Data HandlingOnly a specific subset of data for the current page is fetched from the server.The entire dataset is fetched and loaded into the client.
PerformanceBetter suited for large datasets; reduces client load and memory usage.Can become inefficient with large datasets and higher memory usage on the client.
Initial Load TimeTypically faster, as only a small amount of data is loaded initially.Slower for large datasets since all data must be loaded at once.
ScalabilityMore scalable for applications with large or growing datasets.Less scalable, especially as data size increases.
Network UsageMore efficient, as only the necessary data is transmitted.Higher network usage initially since all data is sent to the client.
ComplexityRequires backend logic to handle pagination and data requests.Simpler to implement since pagination is managed entirely by the client.
User ExperienceCan result in a smoother experience with large data; page changes may involve brief loading.Typically faster for small datasets, but may lag with larger ones.

Overview of the ngx-pagination Library

The ngx-pagination library is one of the most commonly used Angular libraries. It helps integrate the pagination feature into Angular applications by providing ready-to-use components and utilities for handling conventional pagination activities. Thus, it eases the task of implementing client and server pagination.

Key Features

Some key features of the ngx-pagination library include:

  • Pagination Controls: Users can easily customize controls through the ngx-pagination library.

  • Paginate Pipe: To make it simple to display the paginated data in the template, this pipe will automatically slice the data array based on the number of items per page and the current page.

  • Server-Side Compatibility: While the default client-side pagination is supported, you can flexibly use ngx-pagination to handle server-side pagination.

  • Customization: The library affords various customizations, from setting the number of items per page to defining custom labels for the pagination controls and even styling it to look the same as your application’s theme.

Implementing server-side pagination in Angular apps

In this section, we will set up server-side pagination in an Angular application using the ngx-pagination package. Let’s scaffold a new Angular project by running the command below.

ng new <AppName>

Next, install the ngx-pagination library.

npm i --save ngx-pagination

Import the library in the app.component.ts file.

import { NgxPaginationModule } from 'ngx-pagination';

Also, include it in the imports array.

@Component({
  // other properties
  imports: [
    RouterOutlet,
    NgxPaginationModule
 ],
})

Next, define the pagination configuration object in the AppComponent and set it to any.

paginationConfig: any;

Create a constructor and initialize the paginationConfig object to hold the settings for the pagination application. These include currentPage, itemsPerPage, and totalItems. We will set these values to 1, 8, and 0 respectively.

constructor() {
  this.paginationConfig = {
    currentPage: 1,
    itemsPerPage: 8,
    totalItems: 0
 };
}

Query parameters for pagination

We can access the URL query parameters using the ActivatedRoute service. When a user navigates between pages, the current page number is retrieved from the URL. This ensures that the correct page is displayed even if the user reloads the page or shares the URL.

ActivatedRoute, Router, and HttpClient instances must be injected in the constructor. Importing these dependencies at the top of the app.component.ts file is important.

Next, use the activatedRoute.queryParams within the constructor to subscribe to any change that occurs on the query parameters of the URL. In particular, it should listen to the page parameter. The callback inside this subscription must check if there is any argument called page or not. If a page value exists, change the currentPage property of the paginationConfig object accordingly. It should set the currentPage default value to 1 if it doesn’t have a specific page parameter.

constructor(
  private activatedRoute: ActivatedRoute,
  private router: Router,
  private http: HttpClient
) {
  // Instantiation of the pagination object goes here
    
  this.activatedRoute.queryParams.subscribe(params => {
    this.paginationConfig.currentPage = params['page'] ? params['page'] : 1;
 });
}

Connecting the app to a server

To test the pagination feature, we can use a fake API with a database of airline passengers. It is important to note that for an API to be server-side pageable, it must accept pagination parameters, which are page and size or limit. The response should also contain the total count of items and pages.

The following image shows the response received from the API when logged into the console.

consoles

The logged response contains several objects. Each object holds the details for a passenger, including an _id, name, trips, and an airline array. The airline array contains only one airline object, i.e., each passenger is associated with only one airline. The airline object holds the details about the airline, such as the country, year it was established, name, slogan, etc.

To connect the app to the API, let’s start by making a new file called passenger.model.ts. It will include a model that is an exact copy of the data structure that the API will return. Now copy the code below and paste it into the file.

export interface Airline {
  _id: string;
  name: string;
  country: string;
  logo: string;
  slogan: string;
  headquarters: string;
  website: string;
  established: string;
}

export interface Passenger {
  _id: string;
  name: string;
  trips: number;
  airline: Airline[];
}

export interface PassengerResponse {
  totalPassengers: number;
  totalPages: number;
  data: Passenger[];
}

Then, define a private class property called apiUrl inside the AppComponent, which will store the API endpoint’s URL. Also, specify a passengers property as an empty array of the Passenger object.

private apiUrl = 'https://api.instantwebtools.net/v1/passenger';
passengers: Passenger[] = [];

Next, create a function to fetch the passengers’ data. In this function, construct the URL using the base URL, page number, and number of items on the page.

Then, make a GET request to the constructed URL and assign the data to the passengers array. Finally, update the pagination settings.

fetchPassengers(page: number) {
  // Construct the API URL with query parameters for page and size (items per page)
  const url = `${this.apiUrl}?page=${page}&size=${this.paginationConfig.itemsPerPage}`;

  // Make an HTTP GET request to the constructed URL
  this.http.get<PassengerResponse>(url).subscribe(data => {
    // Assign the fetched passenger data to the 'passengers' array
    this.passengers = data.data;

    // Update pagination settings
    this.paginationConfig.totalItems = data.totalPassengers;
    this.paginationConfig.currentPage = page;
 });
}

When the component is initialized, use the ngOnInit method to load the data for the current page. Then, call the fetchPassengers method inside it.

ngOnInit() {
  this.fetchPassengers(this.paginationConfig.currentPage);
}

Handling Page Changes

To manage page changes on pagination control, create the onPageChange function. This function accepts the page number that requires changing as a parameter, and when someone clicks on a different page, this number is sent to the system automatically.

In this case, we will use the router service to update the URL to a new page number. To do this, we will call the this.router.navigate function and pass in an empty array as the first parameter to communicate that we are still on the same route. The second argument consists of an object with queryParams, where the newPage number is specified for the page query parameter.

Finally, call the fetchPassengers() function and pass newPage as the parameter. This function fetches a new data set corresponding to the page number.

  onPageChange(newPage: number) {
  this.router.navigate([''], { queryParams: { page: newPage } });
  this.fetchPassengers(newPage);
}

Displaying Paginated Data

To display paginated data, create a table for the data we want from the server to be shown. The data we will display are the names of different passengers, the total number of trips they took, and the name of the airline they flew with.

Then, inside an unordered list, we use the *ngFor directive to loop through the passengers array and pipe it through the paginationConfig object. This way, there will only be content appropriate to that particular page.

Finally, place the pagination-controls element below the table element. It generates the pagination controls, including page numbers and navigation buttons. Link the control with the component’s onPageChange method by making it listen to the pageChange event.

<div>
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Trips</th>
        <th>Airline</th>
      </tr>
    </thead>
    <tbody>
      <tr
        *ngFor="
 let passenger of passengers
 | paginate
 : {
 id: 'passengerPagination',
 itemsPerPage: paginationConfig.itemsPerPage,
 currentPage: paginationConfig.currentPage,
 totalItems: paginationConfig.totalItems
 }
 "
      >
        <td>{{ passenger.name }}</td>
        <td>{{ passenger.trips }}</td>
        <td>{{ passenger.airline[0].name }}</td>
      </tr>
    </tbody>
  </table>

  <pagination-controls
    id="passengerPagination"
    (pageChange)="onPageChange($event)"
  ></pagination-controls>
</div>

The following image displays the tabulated data of the names of the passengers, the number of trips they took, and the airline they flew with along with previous, next, and page number navigation buttons. These buttons can be clicked to access other pages where you can see changes in the URL parameters.

server-side-pagination

Conclusion

Angular server-side pagination using ngx-pagination is an effective way of managing huge data sets. This is because when it comes to pagination, performance-wise, this should be done from the server side, thereby reducing the load on the client, especially if there are large database collections. This technique guarantees that just the relevant subset of data is retrieved and shown, which improves user experience and speeds up page loading.

Additional Resources

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay