Angular 8 + Node - Server Side Pagination Tutorial & Example
Example built with Angular 8.0.0
Other versions available:
- Angular: Angular 10, 9, 8, 2/5
- React: React, React + Node
- Vue: Vue, Vue + Node
- AngularJS: AngularJS
- ASP.NET: Razor Pages, ASP.NET MVC
This is a simple example of how to implement server-side pagination in Angular 8 with a Node.js backend API.
The example contains a hard coded array of 150 objects split into 30 pages (5 items per page) to demonstrate how the pagination logic works. Styling of the example is done with Bootstap 4.
The tutorial code is available on GitHub at https://github.com/cornflourblue/angular-8-node-server-side-pagination.
Running the Angular + Node Pagination Example Locally
- Install NodeJS and NPM from https://nodejs.org/en/download/.
- Download or clone the tutorial project source code from https://github.com/cornflourblue/angular-8-node-server-side-pagination.
- Install required npm packages of the backend Node API by running the
npm install
command in the/server
folder. - Start the backend Node API by running
npm start
in the/server
folder, this will start the API on the URL http://localhost:4000. - Install required npm packages of the frontend Angular app by running the
npm install
command in the/client
folder. - Start the Angular frontend app by running
npm start
in the/client
folder, this will build the app with webpack and automatically launch it in a browser on the URL http://localhost:8080.
Server-Side (Node.js) Pagination Logic
Pagination is handled by the backend Node API with the help of the jw-paginate
npm package, for more info on how the pagination logic works see JavaScript - Pure Pagination Logic in Vanilla JS / TypeScript.
Below is the code for the paged items route (/api/items
) in the node server file (/server/server.js
) in the example, it creates a hardcoded list of 150 items to be paged, in a real application you would replace this with real data (e.g. from a database). The route accepts an optional page
parameter in the url query string, if the parameter isn't set it defaults to the first page.
The paginate()
function is from the jw-paginate
package and accepts the following parameters:
totalItems
(required) - the total number of items to be pagedcurrentPage
(optional) - the current active page, defaults to the first pagepageSize
(optional) - the number of items per page, defaults to 10maxPages
(optional) - the maximum number of page navigation links to display, defaults to 10
The output of the paginate function is a pager object containing all the information needed to get the current pageOfItems
out of the items
array, and to display the pagination controls in the Angular frontend, including:
startIndex
- the index of the first item of the current page (e.g.0
)endIndex
- the index of the last item of the current page (e.g.9
)pages
- the array of page numbers to display (e.g.[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
)currentPage
- the current active page (e.g.1
)totalPages
- the total number of pages (e.g.30
)
I've set the pageSize
to 5
in the CodeSandbox example above so the pagination links aren't hidden below the terminal console when the container starts up. In the code on GitHub I didn't set the page size so the default 10 items are displayed per page in that version.
The current pageOfItems
is extracted from the items
array using the startIndex
and endIndex
from the pager
object. The route then returns the pager object and current page of items in a JSON response.
// paged items route
app.get('/api/items', (req, res, next) => {
// example array of 150 items to be paged
const items = [...Array(150).keys()].map(i => ({ id: (i + 1), name: 'Item ' + (i + 1) }));
// get page from query params or default to first page
const page = parseInt(req.query.page) || 1;
// get pager object for specified page
const pageSize = 5;
const pager = paginate(items.length, page, pageSize);
// get page of items from items array
const pageOfItems = items.slice(pager.startIndex, pager.endIndex + 1);
// return pager object and current page of items
return res.json({ pager, pageOfItems });
});
Client-Side (Angular 8) Pagination Components
Since the pagination logic is handled on the server, the only thing the Angular client needs to do is fetch the pager information and current page of items from the backend, and display them to the user.
Angular Home Component
Below is the Angular home component (/client/src/app/home/home.component.ts
) from the example. The loadPage()
method fetches the pager
object and pageOfItems
for the specified page
from the backend API with an HTTP request, and the ngOnInit()
gets the page parameter in the route query params or defaults to the first page if the query param is empty.
By subscribing to the route.queryParams
observable, the home component is notified whenever the page changes so it can load the new page.
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ActivatedRoute } from '@angular/router';
@Component({ templateUrl: 'home.component.html' })
export class HomeComponent implements OnInit {
pager = {};
pageOfItems = [];
constructor(
private http: HttpClient,
private route: ActivatedRoute
) { }
ngOnInit() {
// load page based on 'page' query param or default to 1
this.route.queryParams.subscribe(x => this.loadPage(x.page || 1));
}
private loadPage(page) {
// get page of items from api
this.http.get<any>(`/api/items?page=${page}`).subscribe(x => {
this.pager = x.pager;
this.pageOfItems = x.pageOfItems;
});
}
}
Angular Home Component Template
The home component template (/client/src/app/home/home.component.html
) renders the current page of items as a list of divs with the *ngFor
directive, and renders the pagination controls using the data from the pager
object. Each pagination link sets the page
query parameter in the url with the routerLink
directive and [queryParams]
property.
The CSS classes used are all part of Bootstrap 4.3, for more info see https://getbootstrap.com/docs/4.3/getting-started/introduction/.
<div class="card text-center m-3">
<h3 class="card-header">Angular 8 + Node - Server Side Pagination Example</h3>
<div class="card-body">
<div *ngFor="let item of pageOfItems">{{item.name}}</div>
</div>
<div class="card-footer pb-0 pt-3">
<ul *ngIf="pager.pages && pager.pages.length" class="pagination">
<li [ngClass]="{disabled:pager.currentPage === 1}" class="page-item first-item">
<a routerLink [queryParams]="{ page: 1 }" class="page-link">First</a>
</li>
<li [ngClass]="{disabled:pager.currentPage === 1}" class="page-item previous-item">
<a routerLink [queryParams]="{ page: pager.currentPage - 1 }" class="page-link">Previous</a>
</li>
<li *ngFor="let page of pager.pages" [ngClass]="{active:pager.currentPage === page}" class="page-item number-item">
<a routerLink [queryParams]="{ page: page }" class="page-link">{{page}}</a>
</li>
<li [ngClass]="{disabled:pager.currentPage === pager.totalPages}" class="page-item next-item">
<a routerLink [queryParams]="{ page: pager.currentPage + 1 }" class="page-link">Next</a>
</li>
<li [ngClass]="{disabled:pager.currentPage === pager.totalPages}" class="page-item last-item">
<a routerLink [queryParams]="{ page: pager.totalPages }" class="page-link">Last</a>
</li>
</ul>
</div>
</div>
Need Some Angular 8 Help?
Search fiverr for freelance Angular 8 developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!