Infinite Loading Data Table in Lightning Web Component
- Oliver Jones
- Jul 27, 2020
- 2 min read
Updated: May 30, 2022
In Lightning Web component, we can develop lightning data table with infinite loading capability. This will help users to see more records whenever they scroll to the bottom of the table rows, querying more every time the user scrolls to the end rather than querying all records at once. By just scrolling the screen to the bottom of the table, this will also replace pagination where users must click next/previous button to see more records.

Cons of Using Pagination:
Requires writing up complex lines of code.
Need to develop buttons for next, previous, first page and last page and alignment of these elements.
Need to develop events and functions for each of the above buttons.
Users need to click each button every time they need to navigate.
Features:
Faster load performance due to less number of records loaded for each scroll event.
Loading spinner at the end of the table to indicate records are being fetched.
Toast messages on exceptions and when all records are loaded.
Displaying record count for each scroll event respectively.
Fetches records from server every time (every load more).
Common issues faced during development:
Scroll event getting called multiple times for a single end scroll,
Scroll event getting called along with the constructor on Page load resulting in 2X initial rows,
‘Load more’ results being concatenated incorrectly or duplicated results.
The following code will resolve the above known issues that occurs more often,
The HTML Code (InfiniteTable.html)
<template>
<lightning-card>
<h3 slot="title">
<lightning-icon icon-name="utility:user" size="small">
</lightning-icon>
Infinite Loading Account List
<div style="float:right">
<template if:true={disableLoadMore}>
Showing All
</template>
<template if:false={disableLoadMore}>
Showing {currentCount} of {totalRecordCount}
</template>
</div>
</h3>
<div style="height:310px">
<!--Lightning data table markup-->
<lightning-datatable
data={accountData}
columns={accountColumns}
key-field="Id"
hide-checkbox-column
show-row-number-column
enable-infinite-loading
onloadmore={handleLoadMore}
>
</lightning-datatable>
</div>
<div slot="footer">
{loadMoreStatus} {error}
</div>
</lightning-card>
</template>
JavaScript (InfiniteTable.js):
import { LightningElement, track } from 'lwc';
import fetchAccountRecords from '@salesforce/apex/InfiniteTableController.fetchAccountRecords';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class InfiniteTable extends LightningElement {
@track _accountData = [];
@track error;
@track loadMoreStatus = '';
queryOffset;
currentCount = 0;
queryLimit;
totalRecordCount;
timesCalled = 0;
disableLoadMore = false;
fromConstructor;
@track accountColumns = [{
label : "Account Name",
fieldName : "Name",
type : "text"
}, {
label : "Rating",
fieldName : "Rating",
type : "text"
}, {
label : "Account Source",
fieldName : "AccountSource",
type : "text"
}, {
label : "Created By",
fieldName : "CreatedByName",
type : "text"
}, {
label : "Created Date",
fieldName : "CreatedDate",
type : "date",
typeAttributes : {
year: "numeric",
month: "long",
day: "2-digit",
hour: "2-digit",
minute: "2-digit"
}
}
];
constructor() {
super();
this.queryOffset = 0;
this.queryLimit = 10;
this.fromConstructor = true;
this.fetchRecords();
this.currentCount = 10;
console.log('Called from Infinite Table Constructor '+this.queryOffset);
}
showToast(type, title, message, variant, mode) {
const evt = new ShowToastEvent({
type : type,
title: title,
message: message,
variant: variant,
mode: mode
});
this.dispatchEvent(evt);
}
handleLoadMore(event) {
if(this.disableLoadMore)
return;
if(this.fromConstructor == true) {
this.fromConstructor = false;
return;
}
const { target } = event;
//Display a spinner to signal that data is being loaded
target.isLoading = true;
if(this.totalRecordCount > this.queryOffset) {
this.queryOffset = this.queryOffset + 10;
this.fetchRecords()
.then(()=> {
target.isLoading = false;
if(this.totalRecordCount > (this.currentCount + 10))
this.currentCount = this.currentCount + 10;
else
this.currentCount = this.totalRecordCount;
});
}
else {
target.isLoading = false;
this.disableLoadMore = true;
this.loadMoreStatus = 'No more to load.';
this.showToast('Success', 'Success', 'All Account Records are Loaded!', 'success', 'dismissible');
}
console.log('Called from Infinite Table Handle More '+this.queryOffset);
}
fetchRecords() {
let newData;
return fetchAccountRecords({
queryLimit : this.queryLimit,
queryOffset: this.queryOffset
})
.then(result => {
this.totalRecordCount = result.totalRecordCount;
newData = JSON.parse(JSON.stringify(result.accRecords));
newData.forEach(function(entry) {
//Flatten the data so that it can be directly consumed by the table
if(entry.CreatedBy) {
entry.CreatedByName = entry.CreatedBy.Name;
}
});
let newRecords = [...this._accountData, ...newData];
this._accountData = newRecords;
this.error = undefined;
})
.catch(error => {
this.error = error;
this.showToast('Error', 'Error', 'Error getting records from Server', 'error', 'sticky');
})
}
get accountData() {
return this._accountData.length ? this._accountData : null;
}
}
Apex Controller (InfiniteTableController.cls):
public with sharing class InfiniteTableController {
@AuraEnabled(cacheable = true)
public static AccountWrapper fetchAccountRecords(Integer queryLimit, Integer queryOffset) {
return new AccountWrapper([SELECT count() FROM Account],
[SELECT Name, CreatedBy.Name, Rating, AccountSource, CreatedDate FROM Account
ORDER BY CreatedDate DESC
LIMIT :queryLimit
OFFSET :queryOffset]);
}
public class AccountWrapper {
@AuraEnabled
public Integer totalRecordCount { get; set; }
@AuraEnabled
public List<Account> accRecords { get; set; }
public AccountWrapper(Integer totalRecordCount, List<Account> accRecords) {
this.totalRecordCount = totalRecordCount;
this.accRecords = accRecords;
}
}
}
Output:
When Page Loads,

When all records are scrolled and loaded,

Conclusion:
The Infinite Loading data table can reduce a lot of manual code that we used to build for Tables with Paginations. Combining with LWC, table view development can be made faster.
コメント