Efficient Server-Side Pagination in LWC: No Offset Approach

by Rijwan Mohmmed
efficient-server-side-pagination-in-lwc-no-offset-approach

Hello friends, today we are going to discuss Efficient Server-Side Pagination in LWC: No Offset Approach. Pagination is a common requirement when dealing with large data sets in Salesforce applications. In this blog post, we’ll explore an efficient server-side pagination approach using Lightning Web Components (LWC) without relying on offsets. By avoiding offsets, we can improve performance and simplify our code.

Efficient ServerSide Pagination in LWC: No Offset Approach

Also, check this: Datatable using GraphQL in LWC

Key Highlights :

  1. Performance: No-offset pagination performs well even with large data sets
  2. Simplicity: No need to calculate offsets or handle edge cases.
  3. Consistency: Records remain consistent during pagination.
  4. Reduced Complexity: Eliminating offsets simplifies the code, making it easier to maintain and understand.

Code :

EfficientPaginationController.cls:

/**
 * @description       : This class used for. LWC 
 * @coverage          : UnlimitedPaginationControllerTest.cls | @CC100%
 * @author            : zuberrangreaz8@gmail.com
 * @group             : Techdicer
 * @last modified on  : 19-02-2024
 * @last modified by  : zuberrangreaz8@gmail.com
 * Modifications Log
 * Ver   Date         Author                                    Modification
 * 1.0   19-02-2024   zuberrangreaz8@gmail.com             Initial Version | Story Number | @CC100%
 **/
public with sharing class EfficientPaginationController {

    /**
     * Fetches a list of accounts based on pagination parameters.
     *
     * @param buttonTypePrevious Boolean indicating if it's a request for previous records.
     * @param buttonTypeNext     Boolean indicating if it's a request for next records.
     * @param firstId            Id of the first record in the current page.
     * @param lastId             Id of the last record in the current page.
     * @param recordsLimit       Number of records to retrieve in a single request.
     * @return List of Account records based on the pagination criteria.
     */
    @AuraEnabled
    public static List<Account> fetchAccount(Boolean buttonTypePrevious, Boolean buttonTypeNext, String firstId, String lastId, Integer recordsLimit) {
        List<Account> accountList = new List<Account>();
        if (buttonTypeNext == true) {
            // Fetch records with Id greater than the lastId
            accountList = [SELECT Id, Name, Phone, Fax, Site FROM Account WHERE Id > :lastId ORDER BY Id ASC LIMIT :recordsLimit];
        } else if (buttonTypePrevious == true) {
            // Fetch records with Id less than the firstId
            accountList = [SELECT Id, Name, Phone, Fax, Site FROM Account WHERE Id < :firstId ORDER BY Id DESC LIMIT :recordsLimit];
        } else {
            // Fetch the first page of records
            accountList = [SELECT Id, Name, Phone, Fax, Site FROM Account ORDER BY Id ASC LIMIT :recordsLimit];
        }

        if (!accountList.isEmpty()) {
            return accountList;
        }
        return null;
    }

    /**
     * Retrieves the total count of Account records.
     *
     * @return Integer representing the total count of Account records.
     */
    @AuraEnabled(cacheable = true)
    public static Integer recordCount() {
        Integer countResult = [SELECT COUNT() FROM Account];
        System.debug('countResult => ' + countResult);
        return countResult;
    }
}

pagination.html :

<!--
  @description       : 
  @author            : zuberrangreaz8@gmail.com
  @group             : Techdicer
  @last modified on  : 19-02-2024
  @last modified by  : zuberrangreaz8@gmail.com
  Modifications Log 
  Ver   Date         Author                               Modification
  1.0   19-02-2024  zuberrangreaz8@gmail.com            Initial Version | Story Number
-->
<template>
	<lightning-card title="Pagination with Unlimited Data Account Records" icon-name="standard:account">
		<template if:true={showLoading}>
			<lightning-spinner alternative-text="Loading" size="medium" class="spinnerClass">
			</lightning-spinner>
		</template>
		<div class="slds-m-around_medium">
			<lightning-datatable key-field="id" hide-checkbox-column="true" show-row-number-column="true"
				data={accounts} columns={columns}>
			</lightning-datatable>
		</div>
		<lightning-layout class="slds-p-around_small">
			<div class="slds-align_absolute-center">
				<lightning-layout-item>
					<lightning-button label="Previous" icon-name="utility:chevronleft" data-type="previous"
						onclick={previousHandler} disabled={disablePrevious}>
					</lightning-button>
				</lightning-layout-item>
				<lightning-layout-item flexibility="grow" class="slds-p-left_medium"></lightning-layout-item>
				<lightning-layout-item>
					<lightning-button label="Next" icon-name="utility:chevronright" data-type="next"
						icon-position="right" onclick={nextHandler} disabled={disableNext}></lightning-button>
				</lightning-layout-item>
			</div>
		</lightning-layout>
	</lightning-card>
</template>

pagination.js :

/**
 * @description       : 
 * @author            : techdicerkeeplearning@gmail.com
 * @group             : 
 * @last modified on  : 19-02-2024
 * @last modified by  : techdicerkeeplearning@gmail.com
 * Modifications Log 
 * Ver   Date         Author                               Modification
 * 1.0   19-02-2024   zuberrangreaz8@gmail.com    Initial Version
**/
import { LightningElement, api, track, wire } from 'lwc';
import fetchAccount from "@salesforce/apex/EfficientPaginationController.fetchAccount";
import recordCount from "@salesforce/apex/EfficientPaginationController.recordCount";

// Define columns for the data table
const columns = [
    { label: 'Name', fieldName: 'Name' },
    { label: 'Phone', fieldName: 'Phone' },
    { label: 'Fax', fieldName: 'Fax' },
];

export default class UnlimitedPagination extends LightningElement {

    // Public properties
    @api recordsPerPage = 10;

    // Data table columns
    columns = columns;

    // State variables
    @track accounts = [];
    @track showLoading = false;
    searchKey;
    @api lastId;
    @api firstId;
    @api buttonTypePrevious;
    @api buttonTypeNext;
    totalRecountCount = 0;
    totalPage = 0;
    pageNumber = 1;

    // Initialization when the component is connected
    connectedCallback() {
        this.getRecords();
    }

    // Wire service to retrieve the total record count
    @wire(recordCount)
    wiredAccounts({ error, data }) {
        if (data) {
            // Update total record count and calculate total pages
            this.totalRecountCount = data;
            this.totalPage = Math.ceil(this.totalRecountCount / this.recordsPerPage);

            // Ensure pageNumber is within valid range
            if (this.pageNumber <= 1) {
                this.pageNumber = 1;
            } else if (this.pageNumber >= this.totalPage) {
                this.pageNumber = this.totalPage;
            }
        } else if (error) {
            this.error = error;
            this.data = undefined;
        }
    }

    // Disable the "Previous" button when on the first page
    get disablePrevious() {
        return this.pageNumber == 1;
    }

    // Disable the "Next" button when on the last page
    get disableNext() {
        return this.pageNumber == this.totalPage;
    }

    // Fetch records based on pagination parameters
    getRecords() {
        this.showLoading = true;
        fetchAccount({
            buttonTypePrevious: this.buttonTypePrevious,
            buttonTypeNext: this.buttonTypeNext,
            firstId: this.firstId,
            lastId: this.lastId,
            recordsLimit: this.recordsPerPage
        })
        .then(result => {
            // Update accounts data and pagination details
            this.accounts = result;
            this.firstId = result[0].Id;
            this.lastId = result[result.length - 1].Id;

            // Sort accounts if navigating to the previous page
            if (this.buttonTypePrevious === true) {
                this.accounts.sort((a, b) => (a.Id > b.Id) ? 1 : -1);
            }

            this.showLoading = false;
        })
        .catch(error => {
            this.showLoading = false;
            console.error('Error: ' + error.body.message);
        });
    }

    // Handle click on "Previous" button
    previousHandler() {
        if (this.pageNumber > 1) {
            this.pageNumber = this.pageNumber - 1;
            this.buttonTypePrevious = true;
            this.buttonTypeNext = false;
            this.getRecords();
        }
    }

    // Handle click on "Next" button
    nextHandler() {
        if ((this.pageNumber < this.totalPage) && this.pageNumber !== this.totalPage) {
            this.pageNumber = this.pageNumber + 1;
            this.buttonTypeNext = true;
            this.buttonTypePrevious = false;
            this.getRecords();
        }
    }

    // Handle search key input change
    handelSearchKey(event) {
        this.searchKey = event.target.value;
    }

    // Show all records by resetting pagination
    showAllRecords() {
        this.getRecords();
    }
}

pagination.js-meta.xml :

<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
	<apiVersion>57.0</apiVersion>
	<isExposed>true</isExposed>
	<targets>
		<target>lightning__HomePage</target>
		<target>lightning__Tab</target>
	</targets>
</LightningComponentBundle>

Output :

Reference :

  1. SOQL
What’s your Reaction?
+1
3
+1
0
+1
0
+1
0
+1
2
+1
0

You may also like

3 comments

Aruna April 15, 2024 - 12:13 pm

Can we have a sort function implemented on this pagination

Reply
Aruna April 16, 2024 - 4:08 pm

Is there any possibility to perform sort using this functionality

Reply
Rijwan Mohmmed April 16, 2024 - 6:26 pm

Yes we can

Reply

Leave a Comment