Note:
1. Pagination is done in client side(JavaScript). So, this is not the ideal solution for mass records.
2. Consider checking the page performance before using it in Production.
3. Use less columns for better performance.
4. Use Apex with Offset if you want to handle more data up to 2000.
5. Apex is called just once. So, data will be stale if the page is not refreshed.
Apex Class:
public with sharing class PaginationController {
/*
Method to fetch Account records
*/
@AuraEnabled( cacheable = true )
public static List< Account > fetchAccounts() {
return [ SELECT Id, Name, Industry,
( SELECT Id, FirstName, LastName
FROM Contacts )
FROM Account
ORDER BY Name
LIMIT 100];
}
}
Lightning Web Component:
HTML:
<template>
<div class="slds-box slds-theme--default">
<div class="slds-m-bottom_small">
<lightning-button label="Collapse All" onclick={collapseAll} class="slds-m-around_small">
</lightning-button>
<lightning-button label="Expand All" onclick={expandAll} class="slds-m-around_small">
</lightning-button>
</div>
<lightning-tree-grid
columns={gridColumns}
data={gridData}
key-field="Id"
hide-checkbox-column=true
onrowaction={handleRowAction}>
</lightning-tree-grid>
<div class="slds-m-top_small">
<template if:true={frstBool}>
<lightning-button label="First" variant="brand-outline" onclick={goFirst} class="slds-m-around_small">
</lightning-button>
</template>
<template if:true={prevBool}>
<lightning-button label="Previous" variant="brand-outline" onclick={goPrevious} class="slds-m-around_small">
</lightning-button>
</template>
<template if:true={nextBool}>
<lightning-button label="Next" variant="brand-outline" onclick={goNext} class="slds-m-around_small">
</lightning-button>
</template>
<template if:true={lastBool}>
<lightning-button label="Last" variant="brand-outline" onclick={goLast} class="slds-m-around_small">
</lightning-button>
</template>
</div>
</div>
</template>
JavaScript:
import { LightningElement, wire } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import fetchAccounts from '@salesforce/apex/PaginationController.fetchAccounts';
const PAGE_SIZE = 5;
export default class Pagination extends NavigationMixin( LightningElement ) {
gridColumns = [{
type: 'text',
fieldName: 'Name',
label: 'Name'
},
{
type: 'text',
fieldName: 'Industry',
label: 'Industry'
},
{
type: 'text',
fieldName: 'FirstName',
label: 'FirstName'
},
{
type: 'text',
fieldName: 'LastName',
label: 'LastName'
},
{
type: 'button',
typeAttributes: {
label: 'View'
}
}];
gridData;
initalRecords;
frstBool = false;
lastBool = false;
nextBool = false;
prevBool = false;
offset = 0;
pageSize = PAGE_SIZE;
dataCount = 0;
/*
Wire method fetch Account records
*/
@wire(fetchAccounts)
accountTreeData( { error, data } ) {
if ( data ) {
let tempData = JSON.parse( JSON.stringify( data ) );
console.log( 'Data is ' + JSON.stringify( tempData ) );
/*let tempjson = JSON.parse( JSON.stringify( data ).split( 'Contacts' ).join( '_children' ) );
console.log( 'Temp JSON is ' + tempjson );*/
for ( let i = 0; i < tempData.length; i++ ) {
tempData[ i ]._children = tempData[ i ][ 'Contacts' ];
delete tempData[ i ].Contacts;
}
this.initalRecords = tempData;
} else if ( error ) {
if ( Array.isArray( error.body ) )
console.error( 'Error is ' + error.body.map( e => e.message ).join( ', ' ) );
else if ( typeof error.body.message === 'string' )
console.error( 'Error is ' + error.body.message );
}
if ( this.initalRecords ) {
this.dataCount = this.initalRecords.length;
if ( this.dataCount > this.pageSize ) {
this.nextBool = true;
this.lastBool = true;
}
console.log( 'Count is ' + this.dataCount );
this.fetchData();
}
}
/*
Method to expand all Account records to display related Contact records
*/
expandAll() {
const grid = this.template.querySelector( 'lightning-tree-grid' );
grid.expandAll();
}
/*
Method to collapse all Account records to hide related Contact records
*/
collapseAll() {
const grid = this.template.querySelector( 'lightning-tree-grid' );
grid.collapseAll();
}
/*
Method to handle when View button is clicked
*/
handleRowAction( event ) {
const row = event.detail.row;
console.log( 'Row is ' + JSON.stringify( row ) );
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: row.Id,
actionName: 'view'
}
});
}
/*
Method to navigate to previous set of Account records
*/
goPrevious() {
this.offset -= this.pageSize;
this.nextBool = true;
if( this.offset === 0 ) {
this.prevBool = false;
this.frstBool = false;
} else {
this.nextBool = true;
this.lastBool = true;
}
this.fetchData();
}
/*
Method to navigate to next set of Account records
*/
goNext() {
this.offset += this.pageSize;
this.prevBool = true;
if ( ( this.offset + this.pageSize ) >= this.dataCount ) {
this.nextBool = false;
this.lastBool = false;
} else {
this.prevBool = true;
this.frstBool = true;
}
this.fetchData();
}
/*
Method to navigate to first set of Account records
*/
goFirst() {
this.offset = 0;
this.nextBool = true;
this.prevBool = false;
this.frstBool = false;
this.lastBool = true;
this.fetchData();
}
/*
Method to nanigate to last set of Account records
*/
goLast() {
this.offset = this.dataCount - ( this.dataCount % this.pageSize );
this.nextBool = false;
this.prevBool = true;
this.frstBool = true;
this.lastBool = false;
this.fetchData();
}
/*
Method to fetch the data from the original list of records.
slice() is used to get the right set of records based on page size and offset values.
*/
fetchData() {
this.gridData = this.initalRecords.slice( this.offset, ( this.offset + this.pageSize ) );
console.log( this.gridData.length + ' - ' + JSON.stringify( this.gridData ) );
}
}
js.meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>50.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__Tab</target>
</targets>
</LightningComponentBundle>
Output: