VirtualScrolling depends on @angular/cdk's ScrollingModule so begin with installing CDK if not already installed.
Import
import {VirtualScrollerModule} from 'primeng/virtualscroller';
Getting Started
Throughout the samples, a car interface having vin, brand, year and color properties are used to define an object to be displayed by the VirtualScroller. Cars are loaded by a CarService that connects to a server to fetch the cars with a Promise. Note that this is for demo purposes only, any data source such as an Observable can be used as an alternative as well.
export interface Car {
vin;
year;
brand;
color;
}
Here is a sample VirtualScroller that displays a list of cars loaded from a remote datasource.
export class VirtualScrollerDemo implements OnInit {
cars: Car[];
constructor(private carService: CarService) { }
ngOnInit() {
this.carService.getCarsLarge().then(cars => this.cars = cars);
}
}
<p-virtualScroller [value]="cars" scrollHeight="500px" [itemSize]="150">
<ng-template pTemplate="item" let-car>
Car content
</ng-template>
</p-virtualScroller>
Lazy Loading
Lazy mode is handy to deal with large datasets where instead of loading the entire data, small chunks of data are loaded on demand by invoking onLazyLoad callback everytime scrolling requires a new chunk. To implement lazy loading, enable lazy attribute, initialize the number of logical rows with a query and finally implement a method callback using onLazyLoad that actually loads a chunk from a datasource. onLazyLoad gets an event object that contains information about the chunk of data to load such as the index and number of items to load. Notice that a new template called loadingItem is also required to display as a placeholder while the new items are being loaded.
<p-virtualScroller [value]="lazyCars" scrollHeight="500px" [itemSize]="150" [rows]="100" [cache]="false"
[lazy]="true" (onLazyLoad)="loadCarsLazy($event)" [totalRecords]="totalLazyCarsLength">
<ng-template let-car pTemplate="item">
Car content
</ng-template>
<ng-template let-car pTemplate="loadingItem">
Loading...
</ng-template>
</p-virtualScroller>
loadData(event) {
//event.first = First row offset
//event.rows = Number of rows per page
//this.lazyCars = load new chunk between first index and (first + rows) last index
}
Properties
Methods
Styling
Following is the list of structural style classes, for theming classes visit theming page.
Source
@Component({
templateUrl: './virtualscrollerdemo.html',
styles: [`
.car-item .ui-md-3 {
text-align: center;
}
.car-item .ui-g-10 {
font-weight: bold;
}
.empty-car-item-index {
background-color: #f1f1f1;
height: 60px;
margin: 36px auto 0 auto;
animation: pulse 1s infinite ease-in-out;
}
.empty-car-item-image {
background-color: #f1f1f1;
width: 120px;
height: 120px;
animation: pulse 1s infinite ease-in-out;
.empty-car-item-text {
background-color: #f1f1f1;
height: 18px;
animation: pulse 1s infinite ease-in-out;
}
.title-container {
padding: 1em;
text-align: right;
}
.sort-container {
text-align: left;
}
@media (max-width: 40em) {
.car-item {
text-align: center;
}
}
`]
})
export class VirtualScrollerDemo implements OnInit {
cars: Car[] = [];
lazyCars: Car[];
brands: string[];
colors: string[];
totalLazyCarsLength: number;
timeout: any;
sortKey: string;
sortOptions: SelectItem[];
constructor(private carService: CarService) { }
ngOnInit() {
this.brands = [
'Audi', 'BMW', 'Fiat', 'Ford', 'Honda', 'Jaguar', 'Mercedes', 'Renault', 'Volvo', 'VW'
];
this.colors = [
'Black', 'White', 'Red', 'Blue', 'Silver', 'Green', 'Yellow'
];
for (let i = 0; i < 10000; i++) {
this.cars.push(this.generateCar());
}
//in a real application, make a remote request to retrieve the number of records only, not the actual records
this.totalLazyCarsLength = 10000;
this.sortOptions = [
{label: 'Newest First', value: '!year'},
{label: 'Oldest First', value: 'year'}
}
generateCar(): Car {
vin: this.generateVin(),
brand: this.generateBrand(),
color: this.generateColor(),
year: this.generateYear()
}
}
generateVin() {
let text = "";
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
generateBrand() {
return this.brands[Math.floor(Math.random() * Math.floor(10))];
}
generateColor() {
return this.colors[Math.floor(Math.random() * Math.floor(7))];
}
generateYear() {
return 2000 + Math.floor(Math.random() * Math.floor(19));
}
loadCarsLazy(event: LazyLoadEvent) {
//in a real application, make a remote request to load data using state metadata from event
//event.first = First row offset
//event.rows = Number of rows per page
//imitate db connection over a network
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.lazyCars = [];
if (this.cars) {
this.lazyCars = this.cars.slice(event.first, (event.first + event.rows));
}
}, 1000);
}
onSortChange() {
if (this.sortKey.indexOf('!') === 0)
this.sort(-1);
else
this.sort(1);
}
sort(order: number): void {
let cars = [...this.cars];
cars.sort((data1, data2) => {
let value1 = data1.year;
let value2 = data2.year;
let result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;
return (order * result);
});
this.cars = cars;
}