We can achieve rows selections across multiple pages when using pagination in lightning-datatable, by storing the selected rows in a variable.
Sample Apex Class:
public class SampleLightningWebComponentController {
@AuraEnabled
public static Map < String, Object > fetchAccounts(
Integer intCount,
String firstAccountId,
String lastAccountId,
String sortDirection,
String strAccountName
) {
Integer intLimit = 5;
String strSOQL = 'SELECT Id, Name, Industry, Type FROM Account';
Map < String, Object > resultMap = new Map < String, Object >();
if ( String.isNotBlank ( strAccountName ) ) {
strSOQL += ' WHERE Name LIKE \'%' + strAccountName + '%\'';
if ( sortDirection == 'ASC' ) {
if ( String.isNotBlank( lastAccountId ) ) {
strSOQL += ' AND Id > \'' + lastAccountId + '\'';
}
} else if ( sortDirection == 'DESC' ) {
if ( String.isNotBlank( firstAccountId ) ) {
strSOQL += ' AND Id < \'' + firstAccountId + '\'';
}
}
if ( intCount == 0 ) {
String strCountSOQL = 'SELECT COUNT() FROM Account';
strCountSOQL += ' WHERE Name LIKE \'%' + strAccountName + '%\'';
intCount = Database.countQuery( strCountSOQL );
resultMap.put( 'intCount', intCount );
}
strSOQL += ' ORDER BY Id ' + sortDirection + ' LIMIT ' + intLimit;
System.debug(
'SOQL is ' +
strSOQL
);
List < Account > listAccounts = Database.query( strSOQL );
if ( listAccounts.size() > 0 ) {
resultMap.put( 'accounts', listAccounts );
resultMap.put( 'message', 'Successfully fetched Accounts' );
if ( sortDirection == 'ASC' ) {
resultMap.put( 'firstAccountId', listAccounts.get( 0 ).Id );
resultMap.put( 'lastAccountId', listAccounts.get( listAccounts.size() - 1 ).Id );
} else if ( sortDirection == 'DESC' ) {
resultMap.put( 'lastAccountId', listAccounts.get( 0 ).Id );
resultMap.put( 'firstAccountId', listAccounts.get( listAccounts.size() - 1 ).Id );
}
} else {
resultMap.put( 'message', 'No matching Accounts found' );
}
} else {
resultMap.put( 'message', 'Account name is missing' );
}
return resultMap;
}
}
Sample Lightning Web Component:
HTML:
<template>
<template lwc:if={isLoaded}>
<lightning-spinner
alternative-text="Loading"
size="large">
</lightning-spinner>
</template>
<lightning-card
title="Search Accounts"
icon-name="standard:account">
<lightning-layout vertical-align="center">
<lightning-layout-item padding="around-small">
<lightning-input
type="text"
label="Account Name"
class="slds-p-around_medium"
value={accountName}
onchange={handleChange} >
</lightning-input>
</lightning-layout-item>
<lightning-layout-item padding="around-small">
<br/>
<lightning-button
variant="brand"
label="Search Accounts"
class="slds-p-around_medium"
icon-name="utility:record_lookup"
onclick={handleAccountsSearch} >
</lightning-button>
</lightning-layout-item>
</lightning-layout>
</lightning-card>
<template if:true={accounts}>
<lightning-card
title="Searched Accounts"
icon-name="standard:account">
<lightning-datatable
key-field="Id"
data={accounts}
columns={columns}
onrowselection={handleSelected}
selected-rows={selectedAccountIds}>
</lightning-datatable>
<div slot="footer">
<lightning-button
label="Previous"
class="slds-p-around_medium"
onclick={handleAccountsSearch}
disabled={previousButtonDisabled}>
</lightning-button>
<lightning-button
label="Next"
class="slds-p-around_medium"
disabled={nextButtonDisabled}
onclick={handleAccountsSearch} >
</lightning-button>
</div>
</lightning-card>
</template>
<template if:true={selectedAccounts}>
<lightning-card
title="Selected Accounts"
icon-name="standard:account">
<lightning-datatable
key-field="Id"
columns={columns}
data={selectedAccounts}
hide-checkbox-column
show-row-number-column>
</lightning-datatable>
</lightning-card>
</template>
</template>
JavaScript:
import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import fetchAccounts from '@salesforce/apex/SampleLightningWebComponentController.fetchAccounts';
const COLUMNS = [
{ label: 'Name', fieldName: 'Name' },
{ label: 'Industry', fieldName: 'Industry' },
{ label: 'Type', fieldName: 'Type' }
];
export default class SampleLightningWebComponent extends LightningElement {
error;
accounts;
accountName;
intCount = 0;
intOffset = 0;
lastAccountId;
firstAccountId;
isLoaded = false;
selectedAccounts;
columns = COLUMNS;
selectedAccountIds = [];
nextButtonDisabled = true;
previousButtonDisabled = true;
currentAccountIds = [];
currentSelectedAccounts = [];
currentSelectedAccountIds = [];
handleChange( event ) {
if ( event.target.label == 'Account Name' ) {
this.accountName = event.target.value;
}
}
handleAccountsSearch( event ) {
this.isLoaded = true;
let sortDirection = 'ASC';
let buttonName = event.target.label;
this.accounts = undefined;
console.log(
'Button Clicked is',
buttonName
);
console.log(
'Account Name is',
this.accountName
);
if ( buttonName == 'Search Accounts' ) {
this.intOffset = 0;
this.lastAccountId = '';
this.selectedAccounts = [];
this.selectedAccountIds = [];
} else if ( buttonName == 'Previous' ) {
sortDirection = 'DESC';
} else if ( buttonName == 'Next' ) {
}
fetchAccounts( {
intCount : this.intCount,
firstAccountId : this.firstAccountId,
lastAccountId : this.lastAccountId,
sortDirection : sortDirection,
strAccountName : this.accountName
} )
.then( result => {
console.log(
'result is',
JSON.stringify( result )
);
if ( result.accounts ) {
if ( result.accounts.length > 0 ) {
this.accounts = result.accounts.sort( ( a, b ) => {
a = a[ 'Name' ] ? a[ 'Name' ].toLowerCase() : 'z';
b = b[ 'Name' ] ? b[ 'Name' ].toLowerCase() : 'z';
return a > b ? 1 : -1;
});
this.currentAccountIds = [];
this.accounts.forEach( ( row ) => {
this.currentAccountIds.push( row.Id );
} );
this.error = undefined;
if ( buttonName == 'Previous' ) {
this.intOffset -= 5;
} else {
this.intOffset += 5;
}
if ( result.firstAccountId ) {
this.firstAccountId = result.firstAccountId;
}
if ( result.lastAccountId ) {
this.lastAccountId = result.lastAccountId;
}
if ( result.intCount ) {
this.intCount = result.intCount;
}
console.log(
'Offset is',
this.intOffset
);
console.log(
'Count is',
this.intCount
);
if ( this.intCount > this.intOffset ) {
this.nextButtonDisabled = false;
} else {
this.nextButtonDisabled = true;
}
if ( this.intOffset > 5 ) {
this.previousButtonDisabled = false;
} else {
this.previousButtonDisabled = true;
}
} else {
this.dispatchEvent(
new ShowToastEvent( {
title: 'Error',
message: 'No matching Accounts found',
variant: 'error'
} )
);
this.accounts = undefined;
}
} else {
this.dispatchEvent(
new ShowToastEvent( {
title: 'Error',
message: result.message,
variant: 'error'
} )
);
this.accounts = undefined;
this.error = undefined;
}
this.isLoaded = false;
} )
.catch( error => {
console.log(
'Error is',
JSON.stringify( error )
);
this.dispatchEvent(
new ShowToastEvent( {
title: 'Error!!',
message: JSON.stringify( error ),
variant: 'error',
mode: 'sticky'
} )
);
this.accounts = undefined;
this.error = undefined;
this.isLoaded = false;
} )
}
handleSelected( event ) {
this.isLoaded = true;
this.currentSelectedAccounts = [];
this.currentSelectedAccountIds = [];
let tempSelectedAccountIds = this.selectedAccountIds;
let tempSelectedAccounts = this.selectedAccounts;
console.log(
'Selected Rows are',
JSON.stringify(
event.detail.selectedRows
)
);
event.detail.selectedRows.map( row => {
if ( row.Type !== 'Prospect' ) {
this.currentSelectedAccounts.push( row );
this.currentSelectedAccountIds.push( row.Id );
} else {
this.dispatchEvent(
new ShowToastEvent( {
variant: 'error',
title: 'Account not Available',
message: 'Please select Non-Prospect Accounts only'
} )
);
}
} );
console.log(
'Current Selected Accounts are',
JSON.stringify(
this.currentSelectedAccounts
)
);
console.log(
'Current Selected Account Ids are',
JSON.stringify(
this.currentSelectedAccountIds
)
);
if (
tempSelectedAccountIds.length == 0 &&
this.currentSelectedAccountIds.length > 0
) {
console.log(
'Inside Selected Accounts length 0 & current length > 0'
);
tempSelectedAccounts = this.currentSelectedAccounts;
tempSelectedAccountIds = this.currentSelectedAccountIds;
} else if (
this.selectedAccountIds.length == 0 &&
this.currentSelectedAccountIds.length == 0
) {
this.selectedAccounts = [];
this.selectedAccountIds = [];
} else if (
this.selectedAccountIds.length > 0 &&
this.currentSelectedAccountIds.length > 0
) {
event.detail.selectedRows.forEach( ( row ) => {
if (
!tempSelectedAccountIds.includes( row.Id ) &&
this.currentSelectedAccountIds.includes( row.Id )
) {
console.log(
'Inside selected Account Ids not include' +
' and Current Selection include'
);
if ( row.Id ) {
tempSelectedAccounts.push( row );
tempSelectedAccountIds.push( row.Id );
}
}
} );
}
this.currentAccountIds.forEach( ( rowId ) => {
console.log(
'Inside current Account Ids iteration',
rowId
);
if (
tempSelectedAccountIds.includes( rowId ) &&
!this.currentSelectedAccountIds.includes( rowId )
) {
console.log(
'Inside selected Account Ids include' +
' and Current Selection not include'
);
if ( rowId ) {
tempSelectedAccounts.splice(
tempSelectedAccountIds.indexOf( rowId ), 1
);
tempSelectedAccountIds.splice(
tempSelectedAccountIds.indexOf( rowId ), 1
);
}
}
} );
console.log(
'tempSelectedAccountIds are',
JSON.stringify( tempSelectedAccountIds )
);
if ( tempSelectedAccountIds.length > 0 ) {
this.selectedAccounts = [ ...tempSelectedAccounts ];
this.selectedAccountIds = [ ...tempSelectedAccountIds ];
} else {
this.selectedAccounts = [];
this.selectedAccountIds = [];
}
console.log(
'Selected Accounts are',
JSON.stringify(
this.selectedAccounts
)
);
console.log(
'Selected Account Ids are',
JSON.stringify(
this.selectedAccountIds
)
);
this.isLoaded = false;
}
}
js-meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>62.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__Tab</target>
</targets>
</LightningComponentBundle>
Output: