Pagination is the first step to start the LWC Coding (Definitely after the Hello World :D). Pagination is a pretty cool bundle of logic, UI, and Apex to start your assignments with.
With API version 45.0, the Lightning components can be build using 2 Salesforce programming models: –
- Lightning Web Components model
- Aura Components model
LWC uses client JavaScript which your browser understands natively. This makes LWC super fast to work.
In Client-Side Pagination, we will fetch all data at once and then navigate between records in JS only. Pagination makes our UI more user-friendly and Client-side pagination makes it fast. Remember to use this only when you have a limited number of records not in lakhs.
Client-Side Pagination component features:
- Generic component
- Dynamic object Selection
- Dynamic Fields Selection
- Super Fast to navigate between records
- Accordion to switch between your data table and object/field selection.
- Set the number of records to display at once i.e., 10, 25, 50, 100
Demo GIF:
You can Dynamically select any object and fields of that object.
Step-1: Create an Apex controller
Our apex controller will fetch the object list, fields list, and records on processing. Here we are using a wrapper class which will return data to our LWC.
Create ClientSidePaginationController.cls Apex class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
/* Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header */ public with sharing class ClientSidePaginationController { @AuraEnabled public static List<PicklistOptions> fetchObjectList() { List<PicklistOptions> objectList = new List<PicklistOptions>(); for(Schema.SObjectType objTyp : Schema.getGlobalDescribe().Values()) { Schema.DescribeSObjectResult describeSObjectResultObj = objTyp.getDescribe(); if(describeSObjectResultObj.isQueryable() && describeSObjectResultObj.isUpdateable() && describeSObjectResultObj.isSearchable() && describeSObjectResultObj.isAccessible() && describeSObjectResultObj.isCreateable()) { String name = objTyp.getDescribe().getName(); if(!name.containsignorecase('history') && !name.containsignorecase('tag')&& !name.containsignorecase('share') && !name.containsignorecase('feed')) { objectList.add( new PicklistOptions(describeSObjectResultObj.getLabel(), describeSObjectResultObj.getName(), '') ); } } } objectList.sort(); return objectList; } @AuraEnabled public static List<PicklistOptions> fetchFieldsList(String objectName) { List<PicklistOptions> fieldsList = new List<PicklistOptions>(); if(String.isNotBlank(objectName)){ Map<String, Schema.SObjectField> fieldMap = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap(); for (String fieldName: fieldMap.keySet()) { Schema.DescribeFieldResult field = fieldMap.get(fieldName).getDescribe(); if (field.isAccessible() && field.isCreateable()) { fieldsList.add(new PicklistOptions(field.getLabel(), field.getName(), apexTypetoJSType.get(String.valueof(field.getType())) )); } } } fieldsList.sort(); return fieldsList; } @AuraEnabled public static List<sObject> fetchRecords(String objectName, List<String> fieldsList) { try { if(String.isNotBlank(objectName) && fieldsList != null){ String query = 'SELECT id, ' + String.join(fieldsList, ',') + ' FROM '+objectName + ' LIMIT 50000'; return Database.query(query); } return null; } catch(Exception ex) { throw new AuraHandledException(ex.getMessage()); } } public class PicklistOptions implements Comparable { @AuraEnabled public String label; @AuraEnabled public String value; @AuraEnabled public String datatype; public PicklistOptions(String label, String value, String datatype) { this.label = label; this.value = value; this.datatype = datatype; } public Integer compareTo(Object ObjToCompare) { return label.CompareTo(((PicklistOptions)ObjToCompare).label); } } public class ReturnWrapper { @AuraEnabled public List<sObject> records; } public static Map<String, String> apexTypetoJSType = new Map<String, String> { 'ADDRESS' => 'text', 'ID' => 'text', 'TEXTAREA' => 'text', 'STRING' => 'text', 'REFERENCE' => 'text', 'PICKLIST' => 'text', 'MULTIPICKLIST' => 'text', 'DATE' => 'date', 'DOUBLE' => 'number', 'PERCENT' => 'percent', 'PHONE' => 'phone', 'URL' => 'url', 'INTEGER' => 'number', 'CURRENCY' => 'currency', 'DATETIME' => 'date', 'BOOLEAN' => 'boolean', 'EMAIL' => 'email' }; } /* Code by CafeForce Website: http://www.cafeforce.com DO NOT REMOVE THIS HEADER/FOOTER FOR FREE CODE USAGE */ |
Step-2: Now we will create the Client-Side Pagination Lightning Web Component.
Create a new web component clientSidePagination.
Now open the HTML file clientSidePagination.html and paste the below code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<!-- Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header --> <template> <div style="background-color: #fff; position: relative;" class="slds-p-around_medium"> <div if:true={showSpinner}> <lightning-spinner alternative-text="Loading" size="small"></lightning-spinner> </div> <lightning-accordion class="pagination-accordion" active-section-name="A"> <lightning-accordion-section name="A" label="Object and Fields Selection"> <lightning-combobox name="object" label="Status" value={selectedbject} placeholder="Select Object" options={objectList} onchange={handleObjectChange} ></lightning-combobox> <lightning-dual-listbox name="languages" label="Select Fields" class="slds-m-top_medium" source-label="Available" selected-label="Selected" field-level-help="Select Fields" options={fieldsList} value={values} onchange={handleFieldsChange}></lightning-dual-listbox> <br /> <lightning-button variant="brand" label="Fetch Records" disabled={disableProcessBtn} onclick={fetchRecords} ></lightning-button> </lightning-accordion-section> <lightning-accordion-section name="B" label="Records Listview"> <div style="display:flow-root;"> <div style="float:left"> <lightning-combobox name="object" variant="label-hidden" disabled={disableCombobox} value={recordSize} placeholder="Record Size" options={getRecordSizeList} onchange={handleRecordSizeChange} ></lightning-combobox> </div> <div style="float:right"> <lightning-button variant="brand" label="First" disabled={disablePreviousButtons} class="slds-m-top_medium" onclick={handleNavigation} ></lightning-button> <lightning-button variant="brand" label="Previous" disabled={disablePreviousButtons} class="slds-m-top_medium" onclick={handleNavigation} ></lightning-button> <lightning-button variant="brand" label="Next" disabled={disableNextButtons} class="slds-m-top_medium" onclick={handleNavigation} ></lightning-button> <lightning-button variant="brand" label="Last" disabled={disableNextButtons} class="slds-m-top_medium" onclick={handleNavigation} ></lightning-button> </div> </div> <br /> <div> <lightning-datatable key-field="id" hide-checkbox-column=true data={displayRecords} columns={columns}> </lightning-datatable> </div> <br /> <div style="text-align:right;"> {recordViewMessage} </div> </lightning-accordion-section> </lightning-accordion> </div> </template> <!-- Code by CafeForce Website: http://www.cafeforce.com DO NOT REMOVE THIS HEADER/FOOTER FOR FREE CODE USAGE --> |
Now open the JS file clientSidePagination.js and paste the below code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
/* Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header */ import { LightningElement, track } from 'lwc'; import fetchObjectList from '@salesforce/apex/ClientSidePaginationController.fetchObjectList'; import fetchFieldsList from '@salesforce/apex/ClientSidePaginationController.fetchFieldsList'; import fetchRecords from '@salesforce/apex/ClientSidePaginationController.fetchRecords'; import { ShowToastEvent } from 'lightning/platformShowToastEvent'; export default class ClientSidePagination extends LightningElement { @track objectList; @track selectedObject; @track fieldsList; @track selectedFields; @track pageNumber = 1; @track recordSize = '10'; @track displayRecords; @track records; @track totalRecords; @track totalPages; @track columns; @track values; @track showSpinner; constructor() { super(); this.showSpinner = true; fetchObjectList() .then(result => { this.objectList = result; this.showSpinner = false; }).catch(error => { console.log(error); this.showSpinner = false; }) } handleObjectChange(event) { this.selectedFields = []; this.values = []; this.showSpinner = true; this.selectedObject = event.detail.value; fetchFieldsList({ objectName : event.detail.value }) .then(result => { this.fieldsList = result; this.showSpinner = false; }).catch(error => { console.log(error); this.showSpinner = false; }) } handleFieldsChange(event) { this.selectedFields = event.detail.value; } get getRecordSizeList() { let recordSizeList = []; recordSizeList.push({'label':'10', 'value':'10'}); recordSizeList.push({'label':'25', 'value':'25'}); recordSizeList.push({'label':'50', 'value':'50'}); recordSizeList.push({'label':'100', 'value':'100'}); return recordSizeList; } handleNavigation(event){ let buttonName = event.target.label; if(buttonName == 'First') { this.pageNumber = 1; } else if(buttonName == 'Next') { this.pageNumber = this.pageNumber >= this.totalPages ? this.totalPages : this.pageNumber + 1; } else if(buttonName == 'Previous') { this.pageNumber = this.pageNumber > 1 ? this.pageNumber - 1 : 1; } else if(buttonName == 'Last') { this.pageNumber = this.totalPages; } this.processRecords(); } handleRecordSizeChange(event) { this.recordSize = event.detail.value; this.pageNumber = 1; this.totalPages = Math.ceil(this.totalRecords / Number(this.recordSize)); this.processRecords(); } get disablePreviousButtons() { if(this.selectedFields == undefined || this.selectedFields.length == 0 || this.pageNumber == 1) return true; } get disableNextButtons() { if(this.selectedFields == undefined || this.selectedFields.length == 0 || this.pageNumber == this.totalPages) return true; } get disableCombobox() { if(!this.records || this.records.length == 0) return true; } get recordViewMessage() { return 'Total Records - ' + this.totalRecords + ' | Current Page - ' + this.pageNumber + '/' + this.totalPages; } processRecords() { var uiRecords = []; var startLoop = ((this.pageNumber - 1) * Number(this.recordSize)); var endLoop = (this.pageNumber * Number(this.recordSize) >= this.totalRecords) ? this.totalRecords : this.pageNumber * Number(this.recordSize); for(var i = startLoop; i < endLoop; i++) { uiRecords.push(JSON.parse(JSON.stringify(this.records[i]))); } this.displayRecords = JSON.parse(JSON.stringify(uiRecords)); } fetchRecords(event) { this.showSpinner = true; fetchRecords({ objectName : this.selectedObject, fieldsList : this.selectedFields }) .then(result => { if(result != null && result != undefined) { this.records = JSON.parse(JSON.stringify(result)); var uiRecords = []; for(var i = 0; i < Number(this.recordSize); i++) { uiRecords.push(JSON.parse(JSON.stringify(result[i]))); } this.displayRecords = JSON.parse(JSON.stringify(uiRecords)); this.totalRecords = result.length; this.totalPages = Math.ceil(result.length / Number(this.recordSize)); var fieldsColumn = []; for(var i = 0; i < this.fieldsList.length; i++) { for(var j = 0; j < this.selectedFields.length; j++) { if(this.fieldsList[i].value == this.selectedFields[j]) { fieldsColumn.push(this.fieldsList[i]); } } } var columnList = []; for(var j = 0; j < fieldsColumn.length; j++) { columnList.push({'label': fieldsColumn[j].label, 'fieldName': fieldsColumn[j].value, 'type': fieldsColumn[j].datatype}); } this.columns = columnList; } const accordion = this.template.querySelector('.pagination-accordion'); accordion.activeSectionName = 'B'; this.showSpinner = false; }).catch(error => { console.log(error); if(error && error.body && error.body.message) this.showNotification(error.body.message, 'error'); this.showSpinner = false; }) } showNotification(message, variant) { const evt = new ShowToastEvent({ 'message': message, 'variant': variant }); this.dispatchEvent(evt); } } /* Code by CafeForce Website: http://www.cafeforce.com DO NOT REMOVE THIS HEADER/FOOTER FOR FREE CODE USAGE */ |
Open the meta file clientSidePagination.js-meta.xml and paste the below code.
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>49.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__AppPage</target> <target>lightning__RecordPage</target> <target>lightning__HomePage</target> </targets> </LightningComponentBundle> |
Step-3: Now we will call the component.
Create a test aura application test.app and paste the below code in it.
1 2 3 4 5 6 |
<!-- Code by CafeForce || www.cafeforce.com || support@cafeforce.com || Mandatory Header --> <aura:application extends="force:slds"> <c:clientSidePagination></c:clientSidePagination> </aura:application> |
To see the output of the component, go to record page > click on setting gear on top and click on Edit Page. Now drop your component on the screen. Save and Activate the Page to Org Default. Now go back to your record. Your component is visible now.
You can also place this component on App Page and Home Page.
Also, check – Server-Side Pagination in LWC
Your component is now ready to use. Cheers …. Happy Coding … 🙂