Lightning Web Component - Dynamic DataTable Fields And Columns
One of the most common use cases for the Visualforce, Aura, and LWC is to represent data in a tabular form using Data Tables. We know we can do this by creating an APEX class and providing necessary inputs to the Data Table component. This is fine, but how do I control the columns, fields, and the SOQL which fills my Data Table with values.
For example, I created a Data Table with 5 columns showing data from the Account object. But later post everything is in production, I want to add or remove columns to my Data Table. This means I need to go back to the code make changes and then redeploy everything. But hang on this doesn't sound right always, why don't I use Custom Settings or Metadata Types to help me achieve this.
Let's see how you can do this. Below you can see that I have created Custom Metadata, this will help me to control my SOQL, fields, and columns for my table.
In the above image, you can see how I have added values for SOQL, fields, and columns. This I will eventually use in my APEX code to generate the Data Table using LWC. There are few things to be considered:
Once we have configured the metadata, the next is to write LWC and APEX code. In my APEX class, I have used the Wrapper to generate my DataTable columns and field values. The LWC provides input to the apex method which in turn gets the relevant Metadata.
The LWC is also able to show the related object field values up to one level. This code can be further modified to achieve more than one level. Currently, in the JS I have hardcoded the Metadata record Name value, but you can also make this dynamic.
APEX Class
Recommended by LinkedIn
public with sharing class DynamicLWCDataTableController {
@AuraEnabled(cacheable=true)
public static DataTableResponse GetWrapperOfSObjectFieldColumnActionValues(String TableName)
{
List<DataTableColumns> lstDataColumns = new List<DataTableColumns>();
List<SObject> lstDataTableData = new List<SObject>();
List<string> columnList = new List<string>();
List<string> fieldAPINameList = new List<string>();
Dynamic_Table_DataTable__mdt TableComponentMetadata = [SELECT Id, DeveloperName, Field_Value__c, Header_Value__c, Query__c, Table_Title__c FROM Dynamic_Table_DataTable__mdt WHERE DeveloperName =: TableName];
if(TableComponentMetadata.Header_Value__c != null)
{
columnList = TableComponentMetadata.Header_Value__c.split(',');
}
if(TableComponentMetadata.Field_Value__c != null)
{
fieldAPINameList = TableComponentMetadata.Field_Value__c.split(',');
}
for(integer i = 0; i < columnList.size(); i++)
{
DataTableColumns datacolumns = new DataTableColumns( columnList[i].trim() ,
fieldAPINameList[i].substringBefore(':').trim(),
fieldAPINameList[i].substringAfter(':').trim());
lstDataColumns.add(datacolumns);
}
for(SObject SObjectItem : Database.query(TableComponentMetadata.Query__c))
{
lstDataTableData.add(SObjectItem);
}
DataTableResponse finalWrapper = new DataTableResponse();
finalWrapper.TableTitle = TableComponentMetadata.Table_Title__c;
finalWrapper.lstDataTableColumns = lstDataColumns;
finalWrapper.lstDataTableData = lstDataTableData;
return finalWrapper;
}
public class DataTableColumns {
@AuraEnabled
public String label {get;set;}
@AuraEnabled
public String fieldName {get;set;}
@AuraEnabled
public String type {get;set;}
public DataTableColumns(String label, String fieldName, String type)
{
this.label = label;
this.fieldName = fieldName;
this.type = type;
}
}
public class DataTableResponse {
@AuraEnabled
public List<DataTableColumns> lstDataTableColumns {get;set;}
@AuraEnabled
public List<sObject> lstDataTableData {get;set;}
@AuraEnabled
public String TableTitle {get;set;}
}
}
JS Controller
import { LightningElement, track, wire } from 'lwc';
import getDynamicTableDataList from '@salesforce/apex/DynamicLWCDataTableController.GetWrapperOfSObjectFieldColumnActionValues';
export default class Dynamic_LWC_Data_Table extends LightningElement {
@track DataTableResponseWrappper;
@track finalSObjectDataList;
@wire(getDynamicTableDataList, {TableName: 'Accounts_List'})
wiredContacts({ error, data })
{
if(data)
{
let sObjectRelatedFieldListValues = [];
for (let row of data.lstDataTableData)
{
const finalSobjectRow = {}
let rowIndexes = Object.keys(row);
rowIndexes.forEach((rowIndex) =>
{
const relatedFieldValue = row[rowIndex];
if(relatedFieldValue.constructor === Object)
{
this._flattenTransformation(relatedFieldValue, finalSobjectRow, rowIndex)
}
else
{
finalSobjectRow[rowIndex] = relatedFieldValue;
}
});
sObjectRelatedFieldListValues.push(finalSobjectRow);
}
this.DataTableResponseWrappper = data;
this.finalSObjectDataList = sObjectRelatedFieldListValues;
}
else if (error)
{
this.error = error;
}
}
_flattenTransformation = (fieldValue, finalSobjectRow, fieldName) =>
{
let rowIndexes = Object.keys(fieldValue);
rowIndexes.forEach((key) =>
{
let finalKey = fieldName + '.'+ key;
finalSobjectRow[finalKey] = fieldValue[key];
})
}
}
LWC
<template>
<lightning-card>
<div class="slds-p-horizontal_small"></div>
<lightning-layout multiple-rows="true" vertical-align="end">
<lightning-layout-item size="12" padding="around-small">
<div if:true={DataTableResponseWrappper}>
<div class="slds-text-heading_small">{DataTableResponseWrappper.TableTitle}</div>
<lightning-datatable data={finalSObjectDataList}
columns={DataTableResponseWrappper.lstDataTableColumns}
key-field="Id"
hide-checkbox-column="true"
></lightning-datatable>
</div>
</lightning-layout-item>
</lightning-layout>
</lightning-card>
</template>
Finally, this is how it looks like:
Hope this will be helpful to you.
“Fairy tales are more than true: not because they tell us that dragons exist, but because they tell us that dragons can be beaten.”― Neil Gaiman
hi , im unable to fetch related fields with customMetaData , is there any alternative way ?
Hi Great solution Really helpful! Thanks 😀
Hi Abhishek Charles Great solution Really helpful! But is there anyway I can add row actions?
Hi Abhishek Charles How can we create same dynamic table without using lightning datatable tag? I want to use custom table.
👌👍