Create a Dynamic Lightning Datatable in Salesforce

|
| By Webner

Lightning Datatable is used to display the Salesforce data in a tabular form and has many standard features that can be applied to the records being displayed in the datatable.

Generally, we create a datatable and use the hard-coded values for object names, but in the dynamic datatable, we can define the object names in our component and fetch the columns and data dynamically.

To create the Dynamic Lightning Datatable for a related records list, we’ll be using the Account object as the parent and the Policy object as the child.

Step 1: Create an Apex class to provide us with the dynamic columns and data. We’ll use some parameters to fetch the data according to our requirements.
childObjectName is the name of the Child Object.
fieldsNames is the list of fields API names that we need to display on our component.
parentObjRelName is the lookup/master details relationship name we’ll use to fetch our child records from the parent object.
parentRecId is the Id of the Parent Record.
public class FetchDataForDynamicDatatable {
@AuraEnabled(cacheable = true)
public static List<sObject> getDataForDatatable(String childObjectName, List<String> fieldsNames, String parentObjRelName, ID parentRecId){
List<sObject> certHolders = new List<sObject>();
if(parentRecId== null || String.isBlank(parentRecId)) return null;
if(childObjectName == null || String.isBlank(childObjectName)) return null;
if(parentObjRelName== null || String.isBlank(parentObjRelName)) return null;
if(fieldsNames.size() == 0) return null;
String query = 'Select ';
for(String fieldName : fieldsNames){
if(fieldsList.indexOf(fieldName) < (fieldsNames.size() - 1)){
query += fieldName + ', ';
}
else{
query += fieldName + ' ';
}
}
query += 'from ' + childObjectName + ' where ' + parentObjRelName+ ' = \'' +parentRecId+ '\'';
List<sobject> sObjectList = Database.query(query);
return sObjectList;
}
@AuraEnabled(cacheable = true)
public static List<Map<String,String>> getColumnDataForDatatable(String childObjectName, List<String> fieldsNames){
List<Map<String,String>> columnsData = new List<Map<String,String>>();
Map<String,Schema.SObjectField> apiToLabelNameMap = Schema.getGlobalDescribe().get(childObjectName).getDescribe().fields.getMap();
for(String fieldName : fieldsNames){
Map<String,String> labelNameMap = new Map<String,String>();
Schema.DescribeFieldResult fieldResult = apiToLabelNameMap.get(fieldName).getDescribe();
labelNameMap.put('label',fieldResult.getLabel());
labelNameMap.put('fieldName',fieldName);
columnsData.add(labelNameMap);
}
return columnsData;
}
}

Step 2: Create an LWC that will contain our Lightning Datatable.
HTML file:
<template>
<lightning-card>
<lightning-datatable
key-field="id"
data={data}
columns={columns}
onrowaction={handleRowAction}>
hide-checkbox-column>
</lightning-datatable>
</lightning-card>
<template if:true={showDeleteModal}>
<section role="dialog" tabindex="-1" aria-modal="true" aria-labelledby="modal-heading-01" class="slds-modal slds-fade-in-open">
<div class="slds-modal__container">
<button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse">
<lightning-icon
icon-name="utility:close"
size="small"
alternative-text="Indicates close"
onclick={hideDelModal}>
</lightning-icon>
<span class="slds-assistive-text">Cancel and close</span>
</button>
<div class="slds-modal__header">
<h1 id="modal-heading-03" class="slds-modal__title slds-hyphenate">Delete Record</h1>
</div>
<div class="slds-modal__content slds-p-around_medium" id="modal-content-id-3">
<p> Are you sure you want to delete this Record?</p>
</div>
<div class="slds-modal__footer">
<button class="slds-button slds-button_neutral" onclick={hideDelModal} aria-label="Cancel and close">Cancel</button>
<button class="slds-button slds-button_brand" onclick={handleDelete}>OK</button>
</div>
</div>
</section>
<div class="slds-backdrop slds-backdrop_open" role="presentation"></div>
</template>
</template>

JS controller file :
import { LightningElement,api,wire, track } from 'lwc';
import { NavigationMixin } from "lightning/navigation";
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from "@salesforce/apex";
import getDataForDatatable from "@salesforce/apex/FetchDataForDynamicDatatable.getDataForDatatable";
import getColumnDataForDatatable from "@salesforce/apex/FetchDataForDynamicDatatable.getColumnDataForDatatable";
import { deleteRecord } from 'lightning/uiRecordApi';
const actions = [
{ label: 'View', name: 'view' },
{ label: 'Delete', name: 'delete' },
];
const columns = [
{ type: 'action',
typeAttributes: { rowActions: actions },
},
];
export default class DynamicDatatable extends NavigationMixin (LightningElement){
@api recordId;
@api objectApiName;
chilRecordId;
data = [];
columns = columns;
childObjectName = 'Policy__c';
parentRelationshipName = 'Account__c';
fieldsNames = ['Name','Policy_Number__c','Premium_Amount__c','Effective_Date__c','Expiry_Date__c'];
recId;
recIdToDelete;
@track showDeleteModal = false;
@track wiredData;
connectedCallback() {
getColumnDataForDatatable({childObjectName : this.childObjectName, fieldsList : this.fieldsList})
.then( results => {
if(results){
this.columns = [...results, ...this.columns];
console.log('results : ',results);
}
});
}
@wire(getDataForDatatable, {childObjectName:'$childObjectName', fieldsNames:'$fieldsNames',
parentRelationshipName : '$parentRelationshipName', parentRecordId : '$recordId'})
getRecords(response) {
this.wiredData = response;
if(this.wiredData.data){
this.data = this.wiredData.data;
}
else if(response.error){
console.log('Error in wire : ',response.error)
}
}
refreshData(){
refreshApex(this.wiredData);
}
closeNewModalBox() {
this.showCreateNewHolderModal = false;
}
openNewModalBox() {
this.showCreateNewHolderModal = true;
}
handleRowAction(event){
const actionName = event.detail.action.name;
console.log('actionName : ',actionName);
const row = event.detail.row;
console.log('row : ',row);
switch (actionName) {
case 'delete':
this.recIdToDelete = row.Id;
this.showDelConfirmModal();
break;
case 'view':
this.showRecordDetails(row.Id);
break;
default:
}
}
showRecordDetails(childRecordId) {
this[NavigationMixin.Navigate]({
type: 'standard__recordPage',
attributes: {
recordId: childRecordId,
objectApiName: this.childObjectName,
actionName: 'view'
}
});
}
showDelConfirmModal(){
this.showDeleteModal = true;
}
hideDelModal(){
this.showDeleteModal = false;
}
handleDelete(){
deleteRecord(this.recIdToDelete)
.then(() => {
this.showDeleteModal = false;
this.refreshData();
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Record deleted',
variant: 'success'
})
);
})
.catch(error => {
this.showDeleteModal = false;
this.dispatchEvent(
new ShowToastEvent({
title: 'Error deleting record',
message: error.body.message,
variant: 'error'
})
);
});
}
}

Js-meta xml file :
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
<target>lightning__Tab</target>
</targets>
</LightningComponentBundle>

We can specify the parent, and child details on our LWC and it will provide us with the data inside the lightning datatable. We can create a custom tab after going to the Edit page to display our component.

This is how our component would look:
Dynamic Datatable

Leave a Reply

Your email address will not be published. Required fields are marked *