top of page
Search

Infinite Loading Data Table in Lightning Web Component

  • Writer: Oliver Jones
    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.



 
 
 

コメント


bottom of page