Effortless LWC Pagination Example for Salesforce Trailblazers: A Simple and Reusable Component
Pagination is a way to divide a large list of items into smaller, easier-to-manage pages. It helps users navigate through content more efficiently by displaying a limited number of items per page. In Lightning Web Components, pagination can organize data for better performance and user experience.
Who is this article for?
The example I provide is easily implementable by any Salesforce professional, regardless of their role. Whether you’re an admin, business analyst (BA), quality assurance (QA) tester, or any other Salesforce stakeholder, you can quickly test or implement the pagination.
It also can be easily understood by non-Salesforce developers because the basic idea is the same for everyone.
In just 10 minutes, you’ll be able to demonstrate the outcome to the customer, while quickly adjusting the desired number of buttons.
Overview of the provided Pagination Component example
A short overview of what we will be discussing in this blog post:
1. Purpose: The Pagination Component demonstrates how to implement pagination in Lightning Web Components (LWC) for managing large datasets efficiently.
2. Structure:
– Template: Visual layout defined using SLDS elements, including navigation buttons and dynamically generated page number buttons.
– JavaScript Files: Logic for pagination functionality, including methods for navigation actions and dynamic page number generation.
3. Key Features:
– Dynamic Page Number Display: Generates page number buttons based on total pages and current page.
– Reusability: Designed for easy integration into various LWC projects, adaptable to different datasets.
– Efficient Navigation: Provides buttons for first, previous, next, and last pages for streamlined dataset navigation.
4. Implementation:
– Integration guidance for managing pagination of datasets like Accounts and Leads or every custom object that can be easily implemented by any Salesforce professional.
– Configuration options for record sizes and total record counts for flexibility.
We will cover a straightforward solution for implementing pagination in LWC applications, enhancing usability and performance with efficient dataset navigation.
Importance of pagination in web development
Pagination is crucial in web development because it significantly improves the usability and loading time of websites. By breaking down content into smaller, more manageable sections, users can find information faster, reducing the overwhelming effect of loading a massive amount of data at once.
For developers, implementing pagination means better website performance and enhanced user satisfaction, as it aligns with how users prefer to consume content: bit by bit, rather than all at once.
Create PaginationController apex class
Before proceeding, let’s be on the same page in case you don’t have Visual Studio Code installed or set, and will add Lightning Studio Free Chrome Extension so that we can easily create and deploy our component.
We’ll create a PaginationController
Apex class that will be responsible for retrieving the necessary accounts to display on the page, with 5 accounts per page.
Open your org then go to Lightning Studio extension -> click on to create the PaginationController
apex class -> giving the name as PaginationController
.
Copy the bellow code from PaginationController.cls
and replace it in the Lightning Studio PaginationController
apex class.
public class PaginationController {
@AuraEnabled(cacheable=true)
public static Account[] getAccountList(){
return [SELECT Id, Name FROM Account];
}
}
PaginationController.clsAs seen here, we’re selecting the records we intend to display on the page. You can opt for any object you require; for now, we’ll proceed with accounts.
Adding @AuraEnabled(cacheable=true)
to our method enables it to be imported into the next JavaScript file, where we’ll actually expose them on the page.
Setting Up the Child Pagination Component
We won’t dive into the technical details of how the LWC Pagination Component works or what each method does. Instead, you’ll simply copy and paste it where needed. Then, I’ll walk you through any additional configurations required to customize it for your needs.
Now we will create a pagination LWC Component using Lightning Studio
Create child pagination LWC Component
From Lightning Studio extension -> click on and Create New Component then set the following fields:
name - pagination
isExposed - true
Targets
: for now don’t choose anything, this will not be directly exposed, instead it will be used in a parent component for which we want to implement the pagination logic.
Click Deploy
Preparing pagination.js file
Replace the existing code of Lightining Studio pagination.js
by Pasting the bellow code from pagination.js
-> Save -> then Deploy
import { LightningElement, api} from 'lwc';
export default class Pagination extends LightningElement {
currentPage = 1;
totalRecords
totalPageNumbers = 5
pageNumbers = [];
disabledPageNumbers = [];
//nrPagesLeft is used to see how pages are left when we are on current page
nrPagesLeft = 0;
//when using @api we make the variable global
@api recordSize = 5 //records per page
totalPage = 0
get records(){
return this.visibleRecords;
}
@api records
//data comming via records param from parent component paginationDemo.html
set records(data){
if(data){
this.totalRecords = data
this.recordSize = Number(this.recordSize);
//Math.ceil() round up 1.3 = 2 or 1.7 is consiered as 2
//totalPage = total number of pages
this.totalPage = Math.ceil(data.length / this.recordSize);
this.updateRecords();
}
}
nextHandler(){
if(this.currentPage < this.totalPage){
this.currentPage = this.currentPage + 1;
this.updateRecords();
}
}
previousHandler(){
if(this.currentPage > 1){
this.currentPage = this.currentPage - 1;
this.updateRecords();
}
}
firstPageHandler(){
if(this.currentPage > 1){
this.currentPage = 1;
this.updateRecords(1);
}
}
lastPageHandler(){
this.currentPage = this.totalPage;
this.updateRecords();
}
//when clicking on a number page
thisPageHandler(event){
if(event.target.currentpage){
this.currentPage = event.target.currentpage;
//disable current page
event.target.disabled = true;
this.disabledPageNumbers.push(this.currentPage);
this.updateRecords();
}
}
//this method is responsible for creating the buton pages that we want to expose on client side,
//let's say we want to expose just 5 number buttons then we set totalPageNumbers = 5; and that is it.
//then the the html foreach will only generate these pages like 1,2,3,4,5
//when we click on page 5 for example then the next 4 pages will be displayed like 5,6,7,8,9, in total 5 with current page.
calculateVisiblePageNumbers(){
this.pageNumbers = [];
this.nrPagesLeft = (this.totalPage - this.currentPage);
//this.totalPageNumbers - 1; just to make sure when we set above on the top the totalPageNumbers = 3
//then we expect 3 number buttons to apear on page
//this.totalPage = total pages
for(let i = 0; i <= this.totalPageNumbers - 1; ++i){
if(i <= this.totalPage && i <= this.nrPagesLeft){
this.pageNumbers.push(i + this.currentPage);
}
}
return this.pageNumbers;
}
get disablePrevious(){
return this.currentPage <=1;
}
get disableNext(){
return this.currentPage >= this.totalPage;
}
get lastLabelPage(){
return "Last Page "+ this.totalPage;
}
get disableLastPage(){
return this.currentPage >= this.totalPage;
}
get disableFirstPage(){
return this.currentPage === 1;
}
updateRecords(){
const start = (this.currentPage - 1) * this.recordSize;
// when page is 1 the start index will be 0, when page is 2 the start index will be 5, where 1 * 5 = 5 (where 5 is sixth element from total records list)
const end = this.recordSize * this.currentPage;
//when record type is 5 and multipling with curent page 1 then the end will be 5, if current page is 2 then * 5 the end will be 10;
//slice() always generates a new copy
this.visibleRecords = this.totalRecords.slice(start, end);
this.dispatchEvent(new CustomEvent('update',{
detail: {
records: this.visibleRecords
}
}))
this.enablePreviouslyDisabledButtons();
this.calculateVisiblePageNumbers();
}
enablePreviouslyDisabledButtons(){
const buttonElements = this.template.querySelectorAll('lightning-button');//all buttons from child component
for (const button of buttonElements) {
if(this.isEligibleNumericPageButton(button)){
//enabling all buttons that are disabled
button.disabled = false;
}
}
}
//this function makes sure the button is not current and is a numeric button
isEligibleNumericPageButton(button){
return button.currentpage !== this.currentPage && button.disabled && button.label !== "Previous" && button.label !== "Next";
}
}
pagination.jsThe pagination.js
file is designed to facilitate easy navigation through large datasets in Salesforce. Here’s a high-level overview of its functionality:
Initialization:
To start off, the component initializes with default values, such as the current page number, total number of records, and record size per page. Moreover, it includes methods to handle navigation actions, allowing users to effortlessly move to the next or previous page, go to the first or last page, and select a specific page.
Record Display:
Moving on to the record display, the component fetches records based on the current page and record size, ensuring that only a subset of records is displayed at any given time. Furthermore, it dynamically updates the displayed records as users navigate through pages.
Page Navigation:
For seamless navigation, navigation buttons are provided to easily transition between pages, including buttons for navigating to the first, previous, next, and last pages. Additionally, numerical buttons are generated dynamically based on the total number of pages, enabling users to jump to specific pages directly.
Customization:
In terms of customization, the component offers flexibility to work with different datasets and record sizes by adjusting the configuration parameters. This allows developers to seamlessly integrate the Pagination Component into various LWC projects with minimal effort, enhancing the overall user experience when dealing with large datasets.
Overall, the Pagination Component simplifies the implementation of pagination functionality in LWC applications, making it accessible and user-friendly for Salesforce professionals of all levels, including administrators, business analysts, quality assurance testers, and developers.
More About UpdateRecords()
updateRecords(){
const start = (this.currentPage - 1) * this.recordSize;
// when page is 1 the start will be 0, when page is 2 the start will be 1 * 5 = 5 (where 5 is sixth element from total records)
const end = this.recordSize * this.currentPage;
//when record type is 5 and multipling with curent page 1 then the end will be 5, if current page is 2 then * 5 the end will be 10;
//slice() always generates a new copy
this.visibleRecords = this.totalRecords.slice(start, end);
this.dispatchEvent(new CustomEvent('update',{
detail: {
records: this.visibleRecords
}
}))
this.enablePreviouslyDisabledButtons();
this.calculateVisiblePageNumbers();
}
JavaScriptThe updateRecords()
method is responsible for updating the displayed records based on the current page and record size settings. Here’s a breakdown of its functionality:
Calculating Start and End Index:
- The method calculates the start and end indices of the records to be displayed on the current page.
- The start index is determined by subtracting 1 from the current page number and then multiplying by the record size.
- For example, if the current page is 1 and the record size (
records displayed per page
) is 5, the start index will be 0 (1 – 1 = 0) for the first page. Why 0, well because we all know 0 is the first element in our records list. - The end index is calculated by multiplying the record size by the current page number. By default, the current page is 1.
- For instance, if the current page is 2 and the record size is 5, the end index will be 10 (5 * 2 = 10).
Extracting Records:
- The
slice()
method is used to extract a subset of records from the total records array. - It takes the start and end indices calculated earlier and returns a new array containing the records to be displayed on the current page.
Dispatching Event:
- After updating the visible records, a CustomEvent named ‘update’ is dispatched.
- This event contains details about the updated records, which can be consumed by parent components or listeners.
Enabling Buttons and Updating Page Numbers:
- The method calls two helper functions,
enablePreviouslyDisabledButtons()
andcalculateVisiblePageNumbers()
. enablePreviouslyDisabledButtons()
ensures that all previously disabled pagination buttons are enabled.calculateVisiblePageNumbers()
recalculates the visible page numbers based on the updated pagination settings.
Overall, updateRecords()
efficiently manages the display of records on the current page, ensuring a seamless user experience when navigating through the dataset.
Preparing child pagination.html file
Copy the bellow pagination.html code and replace and paste it into Lightning Studio pagination.html
<template>
<lightning-layout>
<!-- GO TO FIRST PAGE -->
<lightning-layout-item>
<lightning-button
label="First Page"
onclick={firstPageHandler}
disabled={disableFirstPage}>
</lightning-button>
</lightning-layout-item>
<!-- GO TO PREVIOUS PAGE -->
<lightning-layout-item>
<lightning-button
label="Previous"
icon-name="utility:chevronleft"
onclick={previousHandler}
disabled={disablePrevious}>
</lightning-button>
</lightning-layout-item>
<!-- Go To Page Number # -->
<template for:each={pageNumbers} for:item="page">
<lightning-button id={page}
label={page}
onclick={thisPageHandler}
key={page}
currentpage={page}
>
</lightning-button>
</template>
<!-- NEXT PAGE -->
<lightning-layout-item>
<lightning-button
label="Next"
icon-name="utility:chevronright"
onclick={nextHandler}
disabled={disableNext}>
</lightning-button>
</lightning-layout-item>
<!--GO TO LAST PAGE -->
<lightning-layout-item>
<lightning-button
label={lastLabelPage}
onclick={lastPageHandler}
disabled={disableLastPage}>
</lightning-button>
</lightning-layout-item>
</lightning-layout>
</template>
pagination.htmlSave then Deploy
Preparing the Parent Component that will use the above reusable pagination component.
Creating the parent accountPagination LWC Component
Now, let’s focus here because this is where we’ll learn how to utilize the pagination LWC created earlier. We’ll create the LWC component to display our records, with 5 records per page, whether it’s accounts, leads, or custom object records.
In Lightning Studio click on Create New Component:
component name
: paginationDemoisExposed
: checkedtargets
: Record Page, App Page, Home Page depending on where you want to use the component, for now, you can select all or just App Page
Deploy the component.
Preparing parent PaginationDemo.js file
Now we will copy and paste the paginationDemo.js
import { LightningElement, wire} from 'lwc';
import getAccountList from '@salesforce/apex/PaginationController.getAccountList';
export default class PaginationDemo extends LightningElement {
visibleAccounts
totalAccounts
@wire(getAccountList)
wiredAccount({error, data}){
if(data){
this.totalAccounts = data;
console.log(this.totalAccounts);
}
if(error){
console.error(error);
}
}
updateAccounttHandler(event){
this.visibleAccounts=[...event.detail.records];
//records passed in the child component
console.log(event.detail.records);
}
}
JavaScriptIn this section, we will continue writing the content for our PaginationDemo
component in Lightning Web Components. We have already imported the necessary modules and set up a wire method to fetch account data from an Apex controller. Now, we will create a visibleAccounts
property and a totalAccounts
property to store our fetched data.
The visibleAccounts
property will hold the list of accounts that will be displayed on our page, while the totalAccounts
property will hold the entire list of accounts retrieved from the database. Next, using the @wire
decorator, we call our getAccountList
method from the Apex controller and assign its returned value to the wiredAccount
variable.
Inside this variable, we have access to both error and data properties. In case there is an error while fetching the data, we can display an error message to the user. Otherwise, we can set our visibleAccounts
property to the retrieved data and our totalAccounts
property to the complete list of accounts.
Next, we will create a template element in our HTML file and use the for:each
directive to loop through our visibleAccounts
list and display each account’s information. Additionally, we can add filters and sorting options by utilizing JavaScript functions to manipulate our visibleAccounts
property before displaying it on the page.
By separating our code into different Web Components, we are able to easily manage and update specific functionalities without affecting other parts of our application. This modular approach also allows for better reusability as these components can be used in multiple pages or applications.
Preparing parent PaginationDemo.html file
Copy and paste the below html code in LightningStudio paginationDemo.html
<template>
<lightning-card title="Pagination demo Accounts" icon-name="custom:custom63">
<div class="slds-var-m-around_medium">
<template if:true={visibleAccounts}>
<template for:each={visibleAccounts} for:item="account">
<p key={account.Id}>{account.Name}</p>
</template>
</template>
<div slot="footer" class="slds-var-m-vertical_medium">
<!-- when we want to call an event method we name the attribut starting with on.. then the event name update i.e onupdate -->
<c-pagination records={totalAccounts} onupdate={updateAccounttHandler}>
</c-pagination>
</div>
</div>
</lightning-card>
</template>
PaginationDemo.htmlThis Lightning Web Component (LWC) template represents a demonstration of pagination for accounts. Here’s a description of its structure and functionality:
Lightning Card:
- The component is enclosed within a
lightning-card
element with the title “Pagination demo Accounts” and an icon named “custom:custom63”.
Record Display:
- Within the card body, records from the
visibleAccounts
array are iterated over using atemplate for:each
directive. - Each account’s name is displayed using a paragraph (
<p>
) element, with thekey
attribute set to the account’s ID to ensure efficient rendering.
Pagination Component:
- The Pagination Component (
c-pagination
) is included in the card’s footer using adiv
with the slot attribute set to “footer”. - The
records
attribute is bound to thetotalAccounts
property, which represents the total set of accounts to paginate. - An
onupdate
event handler (updateAccounttHandler
) is specified to handle updates triggered by the Pagination Component.
Event Handling:
- When an update event is emitted by the Pagination Component, the
updateAccounttHandler
method is invoked to handle the update. - This event handler can be defined in the JavaScript file associated with this template to perform any necessary actions based on the updated records.
Overall, this template provides a user-friendly interface for navigating through a list of accounts using pagination, enhancing the readability and usability of the displayed records.
The “records
” attribute specifies the total number of accounts available, while the “onupdate
” attribute calls a method called “updateAccountHandler
” when any changes are made to the pagination and it uses the totalAccounts
list to slice the necessary records for a specific page. For example, if the current page is 1 and we expose 5 records per page, it will slice Accounts from 0 to 4 from totalAccounts
, and when navigating to page 2 then it will slice from 5 to 9, and so on. Remember, 0 is the 1st element from the list.
How to expose more or less page number buttons?
What if you want to customize the number of buttons displayed on the pagination component? For instance, opting to show only three number buttons like this
It’s a simple adjustment: navigate to the pagination.js
file and assign the number 3 to the totalPageNumbers
property:
import { LightningElement, api} from 'lwc';
export default class Pagination extends LightningElement {
currentPage = 1;
totalRecords
totalPageNumbers = 3 //Modify this number to specify the desired number of page number buttons to display.
pagination.js
In Lightning Studio the js file would look like this:
Or, if you don’t need the last, first, previous, or next buttons, just go to pagination.html
file and comment out the button you don’t need. Let’s say we don’t need the Last Button, then we just need to comment the lightning-layout-item
is located just below of the <!--GO TO LAST PAGE -->
comment.
<!--GO TO LAST PAGE -->
//<lightning-layout-item>
// <lightning-button
// label={lastLabelPage}
// onclick={lastPageHandler}
// disabled={disableLastPage}>
// </lightning-button>
//</lightning-layout-item>
pagination.htmlHow to display more than 5 records per current page?
When it comes to deciding and changing the number of records, cards, articles, or news blocks that you want to display per page then all you have to do is to change the @api recordSize = 5
parameter from 5 to the desired number. Let’s say we want to expose 10 Accounts, then navigate to pagination.js
the file and change it.
import { LightningElement, api} from 'lwc';
export default class Pagination extends LightningElement {
currentPage = 1;
totalRecords
totalPageNumbers = 5
pageNumbers = [];
disabledPageNumbers = [];
nrPagesLeft = 0;
@api recordSize = 10 //here you do the change so that to display the x # of records per page
pagination.jsSave and deploy.
Pagination in action
Assuming we are logged in in a Salesforce Org, we will create an App Page and will drag and drop our component to app page and will then be able to navigate through our accounts.
1. Go to setup, and type – App Builder and choose Lightning App Builder
2. Click New and Select App Page then click Next
3. Then, name it Account Pagination click next, and choose a standard page layout view for example Header and Left Sidebar and click Done.
4. From the Left Side Panel find the paginationDemo component, drag and drop it into the top section of the page:
5. Then, click Save then Activate, and when the pop-up windows appear choose the Lightning Experience tab, then select Sales app in the “Add to Lightning Apps” section then click on “Add page to app” from Lighninginstrumentation section, then Save. Now the Account Pagination is added in the Sales app. Navigate to your Sales App and see your App Page’s Pagination in action and test it.
Play Around with the buttons and test each of them, and if you want more customized functionality let me know in the comments or connect with me on Linkedin, and we can discuss and adjust as per your needs.
How this approach can be reusable?
Hence, the Pagination Component (c-pagination) along with its associated JavaScript file (pagination.js) and HTML file (pagination.html) can be reused across various Salesforce projects or pages. This modular approach facilitates seamless integration of pagination functionality into different Lightning Web Components (LWCs) within the Salesforce ecosystem.
By abstracting the pagination logic into a separate component, developers can minimize code duplication and streamline development efforts across multiple projects, thereby enhancing productivity and code maintainability.These files contain the logic and functionality for pagination, which can be imported and utilized in various Lightning Web Components (LWCs) within the Salesforce environment.
By encapsulating the pagination logic into a standalone component, developers can easily integrate it into different projects without needing to rewrite the code. Additionally, the configuration parameters within the pagination.js file, such as record size and number of page buttons, can be customized to suit specific requirements, further enhancing reusability.
In summary, this guide offers an easy solution for adding pagination to Salesforce Lightning Web Components (LWC). With customizable features and clear instructions, users can navigate data effortlessly, improving productivity and user experience.