datatable with pagination and checkbox cover

Custom Data Table With Pagination And Checkbox Functionality In Salesforce Lightning

Lightning Component

Hello guys, today in this post we are going to learn how we can create custom lightning data table with client side JavaScript pagination buttons and row level checkbox functionality using salesforce lightning component. In this component we’ll also persist checkbox checked state on pagination.

Component Features Highlights : 
  • Client Side JavaScript Pagination.
  • Inline Checkbox Functionality.
  • Get Selected Rows in Controller.
  • Total Records Count
  • Selected Records Count
  • Select/Deselect All Checkbox
datatable with pagination and checkbox demo
Lightning Component Output

Apex Controller : dataTableCtrl

/*
API : 49.00
Author : Rahul Joshi
Source : sfdcMonkey.com
Date : Dec,2020
*/
public class dataTableCtrl{
    @AuraEnabled 
    public static List<accountListWrapper> fetchAccountWrapper(){     
        List<accountListWrapper> lstaccountListWrapper = new List<accountListWrapper>();
        // query account records and create 'accountListWrapper' class instance for each record. 
        for(Account acc : [Select id,Name,Type,Phone
                           From Account
                           Limit 1000]){
                               // by default checkbox should be false 
                               lstaccountListWrapper.add(new accountListWrapper(false,acc));
                           } 
        // return the 'lstaccountListWrapper' list 
        return lstaccountListWrapper; 
    }
    
    /* wrapper class */  
    public class accountListWrapper {
        @AuraEnabled public boolean isChecked {get;set;}
        @AuraEnabled public  account objAccount{get;set;}
        public accountListWrapper(boolean isChecked, account objAccount){
            this.isChecked = isChecked;
            this.objAccount = objAccount;
        }
    }
}
  • Check code comments.

Lightning Component : tableWithPC.cmp

<!--
API : 49.00
Author : Rahul Joshi
Source : sfdcMonkey.com
Date : Dec,2020
-->
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction"
                access="global" 
                controller="dataTableCtrl">
    <!-- call doInit method on component load -->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
	<!-- aura attributes to store data/values --> 
	<aura:attribute name="listOfAllAccounts" type="list"/> 
    <aura:attribute name="PaginationList" type="list"/> 
    <aura:attribute name="selectedCount" type="integer" default="0"
                    description="selected Records Count"/>
    <aura:attribute name="startPage" type="Integer" />
    <aura:attribute name="endPage" type="Integer"/>
    <aura:attribute name="totalRecordsCount" type="Integer"/>
    <aura:attribute name="pageSize" type="Integer" default="4"
                    description="number of records to be display on per page"/>
    <aura:attribute name="currentPage" type="integer" default="1"/>
    <aura:attribute name="totalPagesCount" type="integer"/>
    <aura:attribute name="bNoRecordsFound" type="boolean"/>
    
    
    <aura:if isTrue="{!v.bNoRecordsFound}">
        <!--display error message if there is no records available -->
		<div class="slds-notify slds-notify_alert slds-theme_alert-texture slds-theme_info" role="alert">
            <span class="slds-assistive-text">error</span>
            <h2>No record found.</h2>
        </div>
        <aura:set attribute="else">
		 <!-- lightning:button to get selected rows data -->
            <div class="slds-clearfix slds-m-around_small">
                <div class="slds-clearfix">
                    <div class="slds-float_right">
                        <lightning:button variant="destructive"
                                          label="Get Selected Records"
                                          onclick="{! c.getSelectedRecords }"
                                          disabled="{!v.selectedCount == 0}"/>
                    </div>
                </div>
            </div>
         <!-- display total record and selected record count -->    
            <p class="slds-m-around_small">
                <span class="slds-badge slds-badge_lightest" style="display:inline-block">
                    Total Records : {!v.selectedCount > 0 ? v.selectedCount + '/' : ''} {!v.totalRecordsCount} 
                </span>
            </p>
			
           <!-- data table start-->  
            <table class="slds-table slds-table_bordered slds-table_cell-buffer">
                <thead>
                    <tr class="slds-text-title_caps">
					<!--header checkbox for select all-->
                        <th style="width:3.25rem;" class="slds-text-align_right">
                            <div class="slds-form-element">
                                <div class="slds-form-element__control">
                                    <label class="slds-checkbox">
                                        <ui:inputCheckbox disabled="{!v.totalRecordsCount == 0}"
                                                          aura:id="selectAllId"
                                                          change="{!c.selectAllCheckbox}"/>
                                        <span class="slds-checkbox_faux"></span>
                                        <span class="slds-form-element__label"></span>
                                    </label>
                                </div>
                            </div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Name">Name</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Phone">Phone</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Type">Type</div>
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <aura:iteration items="{!v.PaginationList}" var="obj">
                        <tr>
                            <th scope="row" class="slds-text-align_right" style="width:3.25rem;">
                                <div class="slds-form-element">
                                    <div class="slds-form-element__control">
                                        <label class="slds-checkbox">
                                            <ui:inputCheckbox text="{!obj.objAccount.Id}"
                                                              value="{!obj.isChecked}"
                                                              change="{!c.checkboxSelect}"/>
                                            <span class="slds-checkbox_faux"></span>
                                            <span class="slds-form-element__label text"></span>
                                        </label>
                                    </div>
                                </div>
                            </th>
                            <th scope="row">
                                <div class="slds-truncate" title="{!obj.objAccount.Name}">
                                    {!obj.objAccount.Name}
                                </div>
                            </th> 
                            <th scope="row">
                                <div class="slds-truncate" title="{!obj.objAccount.Phone}">
                                    <lightning:formattedPhone value="{!obj.objAccount.Phone}"/>
                                </div>
                            </th>
                            <th scope="row">
                                <div class="slds-truncate" title="{!obj.objAccount.Type}">
                                    {!obj.objAccount.Type}
                                </div>
                            </th>
                        </tr>
                    </aura:iteration>
                </tbody>
            </table>
             <!-- DataTable End -->
            <br/>
            <!--  Pagination Buttons Start -->
            <div class="slds-align_absolute-center">         
                <lightning:button label="Previous"
                                  disabled="{!v.startPage == 0}"  
                                  onclick="{!c.navigation}"
                                  variant="brand"
                                  iconName="utility:back"
                                  name="previous"/>
                
                <span class="slds-badge slds-badge_lightest"
                      style="margin-right: 10px;margin-left: 10px;">
                    Page {!v.currentPage} out of {!v.totalPagesCount}
                </span>
                
                <lightning:button label="Next"
                                  disabled="{!(v.endPage + 1) >= v.totalRecordsCount}" 
                                  onclick="{!c.navigation}"
                                  variant="brand"
                                  iconName="utility:forward"
                                  iconPosition="right"
                                  name="next"/>
            </div>  
            <!--  Pagination Buttons End -->
        </aura:set> 
    </aura:if>
</aura:component>
  • Check code comments.

JavaScript Controller : tableWithPCController.js

({
    doInit: function(component, event, helper) {
        helper.doInitHelper(component, event);
    },

    /* javaScript function for pagination */
    navigation: function(component, event, helper) {
        var sObjectList = component.get("v.listOfAllAccounts");
        var end = component.get("v.endPage");
        var start = component.get("v.startPage");
        var pageSize = component.get("v.pageSize");
        var whichBtn = event.getSource().get("v.name");
        // check if whichBtn value is 'next' then call 'next' helper method
        if (whichBtn == 'next') {
            component.set("v.currentPage", component.get("v.currentPage") + 1);
            helper.next(component, event, sObjectList, end, start, pageSize);
        }
        // check if whichBtn value is 'previous' then call 'previous' helper method
        else if (whichBtn == 'previous') {
            component.set("v.currentPage", component.get("v.currentPage") - 1);
            helper.previous(component, event, sObjectList, end, start, pageSize);
        }
    },

    selectAllCheckbox: function(component, event, helper) {
        var selectedHeaderCheck = event.getSource().get("v.value");
        var updatedAllRecords = [];
        var updatedPaginationList = [];
        var listOfAllAccounts = component.get("v.listOfAllAccounts");
        var PaginationList = component.get("v.PaginationList");
        // play a for loop on all records list 
        for (var i = 0; i < listOfAllAccounts.length; i++) {
            // check if header checkbox is 'true' then update all checkbox with true and update selected records count
            // else update all records with false and set selectedCount with 0  
            if (selectedHeaderCheck == true) {
                listOfAllAccounts[i].isChecked = true;
                component.set("v.selectedCount", listOfAllAccounts.length);
            } else {
                listOfAllAccounts[i].isChecked = false;
                component.set("v.selectedCount", 0);
            }
            updatedAllRecords.push(listOfAllAccounts[i]);
        }
        // update the checkbox for 'PaginationList' based on header checbox 
        for (var i = 0; i < PaginationList.length; i++) {
            if (selectedHeaderCheck == true) {
                PaginationList[i].isChecked = true;
            } else {
                PaginationList[i].isChecked = false;
            }
            updatedPaginationList.push(PaginationList[i]);
        }
        component.set("v.listOfAllAccounts", updatedAllRecords);
        component.set("v.PaginationList", updatedPaginationList);
    },

    checkboxSelect: function(component, event, helper) {
        // on each checkbox selection update the selected record count 
        var selectedRec = event.getSource().get("v.value");
        var getSelectedNumber = component.get("v.selectedCount");
        if (selectedRec == true) {
            getSelectedNumber++;
        } else {
            getSelectedNumber--;
            component.find("selectAllId").set("v.value", false);
        }
        component.set("v.selectedCount", getSelectedNumber);
        // if all checkboxes are checked then set header checkbox with true   
        if (getSelectedNumber == component.get("v.totalRecordsCount")) {
            component.find("selectAllId").set("v.value", true);
        }
    },

    getSelectedRecords: function(component, event, helper) {
        var allRecords = component.get("v.listOfAllAccounts");
        var selectedRecords = [];
        for (var i = 0; i < allRecords.length; i++) {
            if (allRecords[i].isChecked) {
                selectedRecords.push(allRecords[i].objAccount);
            }
        }
        alert(JSON.stringify(selectedRecords));
    }
})
  • check code comments

JavaScript Helper : tableWithPCHelper.js

({
    /* doInitHelper funcation to fetch all records, and set attributes value on component load */
    doInitHelper : function(component,event){ 
        var action = component.get("c.fetchAccountWrapper");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS"){
                var oRes = response.getReturnValue();
                if(oRes.length > 0){
                    component.set('v.listOfAllAccounts', oRes);
                    var pageSize = component.get("v.pageSize");
                    var totalRecordsList = oRes;
                    var totalLength = totalRecordsList.length ;
                    component.set("v.totalRecordsCount", totalLength);
                    component.set("v.startPage",0);
                    component.set("v.endPage",pageSize-1);
                    
                    var PaginationLst = [];
                    for(var i=0; i < pageSize; i++){
                        if(component.get("v.listOfAllAccounts").length > i){
                            PaginationLst.push(oRes[i]);    
                        } 
                    }
                    component.set('v.PaginationList', PaginationLst);
                    component.set("v.selectedCount" , 0);
                    //use Math.ceil() to Round a number upward to its nearest integer
                    component.set("v.totalPagesCount", Math.ceil(totalLength / pageSize));    
                }else{
                    // if there is no records then display message
                    component.set("v.bNoRecordsFound" , true);
                } 
            }
            else{
                alert('Error...');
            }
        });
        $A.enqueueAction(action);  
    },
    // navigate to next pagination record set   
    next : function(component,event,sObjectList,end,start,pageSize){
        var Paginationlist = [];
        var counter = 0;
        for(var i = end + 1; i < end + pageSize + 1; i++){
            if(sObjectList.length > i){ 
                if(component.find("selectAllId").get("v.value")){
                    Paginationlist.push(sObjectList[i]);
                }else{
                    Paginationlist.push(sObjectList[i]);  
                }
            }
            counter ++ ;
        }
        start = start + counter;
        end = end + counter;
        component.set("v.startPage",start);
        component.set("v.endPage",end);
        component.set('v.PaginationList', Paginationlist);
    },
   // navigate to previous pagination record set   
    previous : function(component,event,sObjectList,end,start,pageSize){
        var Paginationlist = [];
        var counter = 0;
        for(var i= start-pageSize; i < start ; i++){
            if(i > -1){
                if(component.find("selectAllId").get("v.value")){
                    Paginationlist.push(sObjectList[i]);
                }else{
                    Paginationlist.push(sObjectList[i]); 
                }
                counter ++;
            }else{
                start++;
            }
        }
        start = start - counter;
        end = end - counter;
        component.set("v.startPage",start);
        component.set("v.endPage",end);
        component.set('v.PaginationList', Paginationlist);
    },
})
  • Check code comments.
Test App :
 <aura:application extends="force:slds">
    <c:tableWithPC/>
<!-- here c: is org. default namespace prefix-->
</aura:application>

Output :

data table with pagination and checkbox demo
Lightning Component Output

Other popular Post :

Like our facebook page for new post updates. & Don’t forget to bookmark this site for your future reference.

Happy Learning 🙂

 

17 comments

  • Hi,

    Thanks for the code…. Works… i just have one doubt… i’m using this for a custom object where there are approx 20k records, but the table only displays 882.

    Can you let me know how i can display all the records?

     

    Thanks in advance

  • Can we add inline ediying for picklist fields in addition to wrapper n pagination. Can we do using lighting datatable fpr picklist values? Though its a limitation.Pla suggest

  • hey Piyush…This is great..can we add inline editing of picklist fields to this component?Should we go the parent/child component route or can it be achived with the same component you described here.

     

    Appreciate your help.

  • Hi Piyush,

    Can we edit the existing records(show as an input box) which displayed in table and when checkbox is checked, we create a new records with updated values which is changed by user for checked record? This functionality can be achieved using above code with some changes ?

  • I Wonder what’s the meaning of this piece of code
                   if (component.find(“selectAllId”).get(“v.value”)) {
                        Paginationlist.push(sObjectList[i]);
                    }else{
                        Paginationlist.push(sObjectList[i]);
                    }

  • Hi piyush,

    From server Side List List is filled but in while passsing frim Client Side to server side list is going null .means Object are there in List but all they are null.

  • how can load the data faster in jquery datatable, I am using more than 10 accordion section in my component, Each section I am showing more than 500 records, but it takes time to load more than 3 min.

    Anyone can help me on this ??

  • Hi Piyush bro, i wanted a small help with the customLookup component that you’ve created.
    The component works wonderfully. Only thing which I cannot find the solution of is that the result of the search thats the ‘Lightning Pill’. Its text is aligned always to the center. i want it in the left. Can you please help!

Leave a Reply