PrimeNG is a widely-used UI component library for Angular applications, providing a rich set of features to build responsive and interactive user interfaces. One of its key components is the data table, which offers robust solutions for displaying and managing data. However, dealing with large datasets can lead to performance bottlenecks. To address this, lazy loading and sorting are essential techniques that optimize data handling and enhance user experience.
Introduction to Lazy Loading and Sorting
Imagine a table displaying thousands of product entries. Loading all this data at once can significantly slow down page load times, frustrating users. Lazy loading tackles this challenge by fetching data in chunks on demand.
Think of it like paginating your data, but without the need for explicit page navigation controls. The table retrieves only the visible data initially, and as users scroll or interact with the table, additional chunks of data are loaded dynamically. This approach dramatically improves website responsiveness, especially for large datasets.
Key Benefits of Lazy Loading:
- Improved Performance: Only a small portion of data is loaded at any given time, reducing memory and processing overhead.
- Faster Initial Load Times: Users can start interacting with the application sooner since the page loads faster.
- Reduced Bandwidth Usage: Only the necessary data is fetched from the server, saving network resources.
Sorting becomes more complex when combined with lazy loading. Traditionally, tables sort the entire dataset before displaying it. With lazy loading, sorting needs to be adapted to work efficiently with the retrieved data chunks. We’ll explore both client-side and server-side sorting strategies to achieve optimal results.
Implementing Lazy Loading in PrimeNG Tables
PrimeNG’s p-table
component supports lazy loading out of the box. To enable this feature, you need to set the lazy
property to true
and handle the onLazyLoad
event, which is triggered whenever new data is required.
Basic Implementation
Here’s a basic implementation of lazy loading with PrimeNG tables:
Component Setup:
import { Component, OnInit } from '@angular/core';
interface Car {
vin: string;
year: number;
brand: string;
model: string;
}
@Component({
selector: 'app-lazy-loading-table',
templateUrl: './lazy-loading-table.component.html',
styleUrls: ['./lazy-loading-table.component.css']
})
export class LazyLoadingTableComponent implements OnInit {
cars: Car[] = [];
totalRecords = 0;
first = 0;
rows = 10;
async loadData(event: LazyLoadEvent) {
this.totalRecords = 0;
let pageNumber = (event.first === 0 || event.first == undefined) ? 0 : event.first / (event.rows == undefined ? 1 : event.rows) + 1;
this.rows = event.rows == undefined ? 10 : event.rows;
let skip = pageNumber * this.rows;
/**Replace with the server side API */
let res = await getCarsData(skip, this.rows, this.filterText, '', '').toPromise();
this.cars = []
for await (let _res of res.data) {
cars.push({
vin: _res.vin,
year: _res.year,
brand: _res.brand,
model: _res.model
})
}
this.totalRecords = res.totalRecords;
if (this.results){
this.load = true
this.loading = false
}
}
ngOnInit() {
}
}
HTML template:
<p-table [lazy]="true" (onLazyLoad)="loadData($event)" paginatorDropdownAppendTo="body" [rows]="rows" [showCurrentPageReport]="true" currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries" [rowsPerPageOptions]="[10,25,50]" [totalRecords]="totalRecords"
[value]="cars" [paginator]="true" >
<ng-template pTemplate="header">
<tr>
<th>Year</th>
<th>Model</th>
<th>Brand</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-car>
<tr>
<td>{{car.year}}</td>
<td>{{car.model}}</td>
<td>{{car.brand}}</td>
</tr>
</ng-template>
</p-table>
In this example:
- The
cars
array holds the currently displayed data chunk. - The
totalRecords
property represents the total number of records in the dataset. - The
first
androws
properties control the pagination behavior (number of records displayed initially). - The
loadData
method simulates data retrieval from a server based on theevent
object received from theonLazyLoad
event. This event provides information about the requested data chunk (first record index, number of rows). - Within the
loadData
method, you would replace the code with your actual logic to fetch data from a backend service.
Advanced Implementation with Server-Side Data
In a real-world application, you’ll likely need to fetch data from a server. Here’s how you can implement server-side lazy loading and sorting:
Server-Side Implementation
Backend API
Here’s an example of how you might structure your server-side code to support lazy loading and sorting. This example uses a Node.js/Express backend with a hypothetical CarRepository
.
async getCarsData(skip, pageSize, filterText, sectorList, assessmentType): Promise<any[]> {
let data = this.carRepository.createQueryBuilder('car')
.innerJoinAndSelect(
'car.model',
'model',
'model.id = car.modelId'
)
.innerJoinAndSelect(
'car.brand',
'brand',
'brand.id = car.brandId'
)
data.skip(skip)
.take(pageSize);
try {
let res= await data.getManyAndCount()
return {
data: res[0],
totalRecords: res[1]
}
} catch (err) {
throw new InternalServerErrorException();
}
}
Sorting with Lazy Loading
Sorting presents a unique challenge with lazy loading. Since only a portion of the data is loaded at a time, sorting the entire dataset becomes impractical. Here, we explore two approaches:
- Client-Side Sorting: This approach sorts the retrieved data chunk (limited dataset) within the Angular application. This method is efficient for small datasets but becomes computationally expensive for massive datasets.
- Server-Side Sorting: For large datasets, server-side sorting is the preferred approach. In this scenario, the
onLazyLoad
event sends sorting information (sort field, sort order) to the server along with pagination details. The server then fetches and sorts the requested data chunk based on the provided criteria before sending it back to the client.
Here’s an example structure for server-side sorting with lazy loading:
Client Side Setup:
In your Angular component, modify the loadData
method to include sorting parameters:
import { Component, OnInit } from '@angular/core';
interface Car {
vin: string;
year: number;
brand: string;
model: string;
}
@Component({
selector: 'app-lazy-loading-table',
templateUrl: './lazy-loading-table.component.html',
styleUrls: ['./lazy-loading-table.component.css']
})
export class LazyLoadingTableComponent implements OnInit {
cars: Car[] = [];
totalRecords = 0;
first = 0;
rows = 10;
async loadData(event: LazyLoadEvent) {
this.totalRecords = 0;
let pageNumber = (event.first === 0 || event.first == undefined) ? 0 : event.first / (event.rows == undefined ? 1 : event.rows) + 1;
this.rows = event.rows == undefined ? 10 : event.rows;
let skip = pageNumber * this.rows;
/**Replace with the server side API */
let res = await getCarsData(skip, this.rows, this.filterText, '', '', event.sortField ? event.sortField : '', event.sortOrder === 1 ? 'ASC' : 'DESC').toPromise();
this.cars = []
for await (let _res of res.data) {
cars.push({
vin: _res.vin,
year: _res.year,
brand: _res.brand,
model: _res.model
})
}
this.totalRecords = res.totalRecords;
if (this.results){
this.load = true
this.loading = false
}
}
ngOnInit() {
}
}
Template Update:
Ensure your table template supports sorting by adding sortable columns:
<p-table [lazy]="true" (onLazyLoad)="loadData($event)" paginatorDropdownAppendTo="body" [rows]="rows" [showCurrentPageReport]="true" currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries" [rowsPerPageOptions]="[10,25,50]" [totalRecords]="totalRecords"
[value]="cars" [paginator]="true" [sortOrder]="1" >
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="year">Year <p-sortIcon field="year"></p-sortIcon></th>
<th pSortableColumn="model">Model<p-sortIcon field="model"></p-sortIcon></th>
<th pSortableColumn="brand">Brand <p-sortIcon field="brand"></p-sortIcon></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-car>
<tr>
<td>{{car.year}}</td>
<td>{{car.model}}</td>
<td>{{car.brand}}</td>
</tr>
</ng-template>
</p-table>
Server side implementation:
async getCarsData(skip, pageSize, filterText, sectorList, assessmentType, sortField, sortOrder): Promise<any[]> {
let data = this.carRepository.createQueryBuilder('car')
.innerJoinAndSelect(
'car.model',
'model',
'model.id = car.modelId'
)
.innerJoinAndSelect(
'car.brand',
'brand',
'brand.id = car.brandId'
)
data.skip(skip)
.take(pageSize);
if (sortField === 'year') {
data.orderBy('car.year', sortOrder);
} else if (sortField === 'model') {
data.orderBy('model.name', sortOrder);
} else if (sortField === 'brand') {
data.orderBy('brand.name', sortOrder);
} else {
data.orderBy('car.id', 'DESC');
}
try {
let res= await data.getManyAndCount()
return {
data: res[0],
totalRecords: res[1]
}
} catch (err) {
throw new InternalServerErrorException();
}
}
Conclusion
By lazy loading and sorting techniques with PrimeNG tables, you can significantly enhance the performance and user experience when handling large datasets in your Angular applications. Remember to choose the appropriate sorting approach (client-side or server-side) based on your data size and specific requirements.
This blog post has provided a comprehensive guide to implementing lazy loading and sorting with PrimeNG tables.Explore the provided code examples and resources to gain a deeper understanding and customize these techniques for your projects.
Feel free to share your experiences and ask any questions in the comments section below. Happy coding!
Subscribe
Enter your email below to receive updates.