Lightning Web Component - Dynamic DataTable Fields And Columns

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.

No alt text provided for this image
No alt text provided for this image

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:

  1. The DataTable attribute Columns consists of different properties like 'label', 'fieldName', and 'type'.
  2. In the Field Value field, the way I have kept the values is in a specific format comma separated.
  3. The format is 'fieldName' +':'+ 'type'. This will help me to determine, what is the field along with its type.
  4. The Header Value field contains the column names comma separated.
  5. The most important thing to consider is that the sequence of the Field Value and the Header Value field needs to be consistent.
  6. That is if the first column is Name then the field should also be Name or as per requirement.
  7. I am also controlling the table title using the metadata. You can add your own logic to control most of the things.

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

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:

No alt text provided for this image

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 ?

Like
Reply

Hi Great solution Really helpful! Thanks 😀

Hi Abhishek Charles Great solution Really helpful! But is there anyway I can add row actions?

Like
Reply

Hi Abhishek Charles How can we create same dynamic table without using lightning datatable tag? I want to use custom table.

Like
Reply

To view or add a comment, sign in

More articles by Abhishek Charles

  • Einstein Case Classification

    In recent times Salesforce has added many capabilities to Service Cloud Einstein. One of them is Einstein Case…

    10 Comments
  • New Features For Platform Developers

    Salesforce during the Winter 22 release came up with many exciting features. As always all the new features are super…

  • Slack Integration With Salesforce

    Hey all, so recently we heard this big noise that Salesforce has acquired Slack in a multi-billion $ deal. SAN…

  • Einstein Bots - Natural Language Intent

    Hello all, so in my last article about the Einstein Bot I mentioned how you can configure your own personalized bot(Mr.…

  • Einstein Bots - The Power Of Artificial Intelligence

    Hey all, I know most of us have watched Iron man. The famous Tony Stark, his super-powerful suits, but was the movie…

  • Salesforce Data Security Model

    Hi all, most of us are very much familiar with the Salesforce data security model. But now or then we all fumble with…

  • Salesforce Dynamic Forms

    Consider a scenario when you need to show a different set of fields on a page layout based on certain field values. We…

    1 Comment
  • Launch Tableau CRM dashboard From A Lightning Experience Page On Click Of A Button

    Ever wondered if we can show the Tableau CRM dashboard from a Lightning Experience page with the click of a button…

Others also viewed

Explore content categories