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.
Discover how at OpenReplay.com.
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.
Aspect | Server-Side Pagination | Client-Side Pagination |
---|---|---|
Data Handling | Only a specific subset of data for the current page is fetched from the server. | The entire dataset is fetched and loaded into the client. |
Performance | Better 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 Time | Typically faster, as only a small amount of data is loaded initially. | Slower for large datasets since all data must be loaded at once. |
Scalability | More scalable for applications with large or growing datasets. | Less scalable, especially as data size increases. |
Network Usage | More efficient, as only the necessary data is transmitted. | Higher network usage initially since all data is sent to the client. |
Complexity | Requires backend logic to handle pagination and data requests. | Simpler to implement since pagination is managed entirely by the client. |
User Experience | Can 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.
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.
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.