Enhanced Dynamic DataTable using LWC to dynamically select columns (Fields) to display for any object
How we can show dynamic DataTable with Lightning Web Component ?
A developer has created a dynamic Datatable for the Business and he left teh organization. Now after few days Admin has added a new Formual Field on an object which is using Hyperlink function. Now this new column in the Table should be added along with the clickable URL.
End user can select the fields to add in the existing custom datatable similar to Standard select fields to display.
Steps to implement
Step 1: Create an Object DataTableConfigurationObject__c
This object is created to store the fields selected by the user . Everytime user updates the columns or selects field this object will be updated.
Step 2: Create two custom fields in this object to store the ObjectAPI and FieldAPIName.
* I have used Custom Object instead of custom settings because the character limit was exceeding for Fields API name and Custom metadata needs to be deployed every time a user updates a field. If you do not want to give end user access to control the columns we can simply use flows to pass the predefined columns and Object name.
Step 3: Create dynamicDataTableController Apex Class. This Class is used to query teh field name , API name and do other upsert operations.
Step 1: Create an Object DataTableConfigurationObject__c
This object is created to store the fields selected by the user . Everytime user updates the columns or selects field this object will be updated.
Step 2: Create two custom fields in this object to store the ObjectAPI and FieldAPIName.
* I have used Custom Object instead of custom settings because the character limit was exceeding for Fields API name and Custom metadata needs to be deployed every time a user updates a field. If you do not want to give end user access to control the columns we can simply use flows to pass the predefined columns and Object name.
Step 3: Create dynamicDataTableController Apex Class. This Class is used to query teh field name , API name and do other upsert operations.
Apex Class
dynamicDataTableController.cls
public with sharing class dynamicDataTableController {
@AuraEnabled
public static String updateSelectedFields(String objectName, String fieldAPINames){
String getNameFieldAPI = '';
try{
for(String str : fieldAPINames.split(',')){
if(str.containsIgnoreCase('.Name')){
if(str.containsIgnoreCase('__c.Name')){
getNameFieldAPI += str.Substring(0,str.length()-5) + ',' ;
}else{
getNameFieldAPI += str.Substring(0,str.length()-5) + 'Id'+',' ;
}
}else{
getNameFieldAPI += str+ ',' ;
}
}
List<DataTableConfigurationObject__c> fieldAPINameStored = new List<DataTableConfigurationObject__c>();
List<DataTableConfigurationObject__c> updatetableConfig = new List<DataTableConfigurationObject__c>();
fieldAPINameStored = [SELECT Id,Name,ObjectAPIName__c,All_Field_API_Name__c FROM DataTableConfigurationObject__c WHERE ObjectAPIName__c =:objectName LIMIT 1];
if(fieldAPINameStored.size() > 0){
for(DataTableConfigurationObject__c dataconfg : fieldAPINameStored){
DataTableConfigurationObject__c objDataTableConfig = new DataTableConfigurationObject__c();
objDataTableConfig.All_Field_API_Name__c = getNameFieldAPI.removeEnd(',');
objDataTableConfig.Id = dataconfg.Id ;
objDataTableConfig.ObjectAPIName__c = objectName;
updatetableConfig.add(objDataTableConfig);
}
}else{
DataTableConfigurationObject__c objDataTableConfig = new DataTableConfigurationObject__c();
objDataTableConfig.All_Field_API_Name__c = getNameFieldAPI.removeEnd(',');
objDataTableConfig.ObjectAPIName__c = objectName;
updatetableConfig.add(objDataTableConfig);
}
if(updatetableConfig.size() > 0){
upsert updatetableConfig;
}
return 'Success';
}catch(Exception ex){
throw new AuraHandledException(ex.getMessage());
}
}
@AuraEnabled
public static List<SObject> retreieveRecords(String objectName, String fieldAPINames){
String fieldApi = '';
String getNameFieldAPI = '';
for(String str : fieldAPINames.split(',')){
if(str.containsIgnoreCase('__c.Name')){
fieldApi = fieldApi + str.Substring(0,str.length()-5)+',';
}else{
fieldApi = fieldApi + str+ ',' ;
}
}
fieldApi = fieldApi.removeEnd(',');
String strQuery = 'SELECT ' + fieldApi + ' FROM ' + objectName ; //+ ' LIMIT 10' ;
System.debug('database.query(strQuery) $$ '+database.query(strQuery) );
return database.query(strQuery);
}
//Retrieve field details based on Object API Name
@AuraEnabled (cacheable=true)
public static String retreieveFields(String ObjectName){
MapFieldWrapperClass fieldMapWrap = new MapFieldWrapperClass();
Map<String, String> existingmapField = new Map<String, String>();
Map<String, String> newmapField = new Map<String, String>();
Map<String, String> allmapField = new Map<String, String>();
Set<String> setFieldList = new Set<String>();
List<String> fieldAPIList = new List<String>();
System.debug('ObjectName =>'+ObjectName );
String RegEx ='<\\/?[^>]*>';
if(!String.isEmpty(ObjectName)){
List<DataTableConfigurationObject__c> fieldAPINameStored = [SELECT Id,Name,ObjectAPIName__c,All_Field_API_Name__c FROM DataTableConfigurationObject__c WHERE ObjectAPIName__c =:ObjectName];
if(fieldAPINameStored.size() > 0 && fieldAPINameStored[0].All_Field_API_Name__c != null ){
System.debug('existing data fieldAPINameStored[0].All_Field_API_Name__c =>' + fieldAPINameStored[0].All_Field_API_Name__c);
String unescapeString = fieldAPINameStored[0].All_Field_API_Name__c.replaceAll(RegEx, '');
System.debug('Unescaped String: => '+unescapeString);
for(String str : unescapeString.split(',')){
fieldAPIList.add(str);
}
existingmapField = sobjectListField(fieldAPIList,ObjectName);
System.debug('existing existingmapField $$ '+existingmapField);
fieldMapWrap.selectedFieldsMap = sortFieldMapInAscending(existingmapField);
System.debug('fieldMapWrap.selectedFieldsMap => '+fieldMapWrap.selectedFieldsMap );
}
System.debug('New data else block ' );
SObjectType sObjType = ((SObject) Type.forName(ObjectName).newInstance()).getSObjectType();
for(Schema.SObjectField fld: sObjType.getDescribe().fields.getMap().values()){
if(fld.getDescribe().getType() == Schema.DisplayType.REFERENCE){
allmapField.put(fld.getDescribe().getLabel().replaceAll('ID',''),fld.getDescribe().getRelationshipName() + '.Name');
}else if(fld.getDescribe().getCalculatedFormula() != null && fld.getDescribe().getCalculatedFormula().containsIgnoreCase('hyperlink')){
allmapField.put(fld.getDescribe().getLabel(),fld.getDescribe().getName() + '.Name');
}else if(fld.getDescribe().getName() != 'Id'){
allmapField.put(fld.getDescribe().getLabel(),fld.getDescribe().getName());
}
}
fieldMapWrap.allAvailableFieldsMap = sortFieldMapInAscending(allmapField);
System.debug('fieldMapWrap.allAvailableFieldsMap => '+fieldMapWrap.allAvailableFieldsMap );
}
return JSON.serialize(fieldMapWrap);
}
public static Map<String, String> sortFieldMapInAscending(Map<String, String> mapField){
Map<String, String> sortedMap = new Map<String, String>();
List<String> fieldlabelList = new List<String>();
fieldlabelList.addAll(mapField.keySet());
fieldlabelList.sort();
for (Integer i = (fieldlabelList.size() - 1); i >= 0; i--){
sortedMap.put(fieldlabelList[i], mapField.get(fieldlabelList[i]));
// System.debug('fieldlabelList ' + i + ' $$' + fieldlabelList[i] + ' == '+ mapField.get(fieldlabelList[i]));
}
System.debug('new sortedMap =>' + sortedMap);
return sortedMap;
}
public static Map<String, String> sobjectListField(List<String> fieldList,String ObjectName){
System.debug('fieldList $$ '+fieldList);
System.debug('ObjectName $$ '+ObjectName);
Schema.DescribeSObjectResult objDescribe = Schema.getGlobalDescribe().get(ObjectName).getDescribe();
Map<String, String> newmapField = new Map<String, String>();
for(String sfld: fieldList){
System.debug('Schema.SObjectField Label $$ '+ objDescribe.fields.getMap().get(sfld).getDescribe().getLabel());
System.debug('sfld $$ '+sfld);
Schema.DescribeFieldResult fieldResult = objDescribe.fields.getMap().get(sfld).getDescribe();
if(fieldResult.getType() == Schema.DisplayType.REFERENCE){
newmapField.put(fieldResult.getLabel().replaceAll('ID',''),fieldResult.getRelationshipName() + '.Name');
}else if(fieldResult.getCalculatedFormula() != null && fieldResult.getCalculatedFormula().containsIgnoreCase('hyperlink')){
newmapField.put(fieldResult.getLabel(),fieldResult.getName() + '.Name');
}else if(fieldResult.getName() != 'Id'){
newmapField.put(fieldResult.getLabel(),fieldResult.getName());
}
}
return newmapField;
}
public class MapFieldWrapperClass {
@AuraEnabled
public Map<String, String> selectedFieldsMap;
@AuraEnabled
public Map<String, String> allAvailableFieldsMap;
}
}
Step 4: Create childDatable. This component holds the button "Select Fields to display" which will pop up the dual box to select the fields to be diaplyed.
childDatatable.html
<template>
<!-- Button to open the dual Box for selecting fields -->
<lightning-button
variant="brand"
label="Select Fields to display"
title="Select Fields to display"
onclick={handleSelectFields}
class="slds-m-left_x-small">
</lightning-button>
<template if:true={isModalOpen}>
<!-- Modal/Popup Box LWC starts here -->
<section role="dialog" tabindex="-1"
aria-labelledby="modal-heading-01" aria-modal="true"
aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
<div class="slds-modal__container">
<!-- Modal/Popup Box LWC header here -->
<header class="slds-modal__header">
<button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse"
title="Close" onclick={closeModal}>
<lightning-icon icon-name="utility:close" alternative-text="close" variant="inverse" size="small"></lightning-icon>
<span class="slds-assistive-text">Close</span>
</button>
<h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Select Fields to display</h2>
</header>
<!-- Modal/Popup Box LWC body starts here -->
<div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
<template if:true={errorStatus}>
<div class="slds-box slds-box_small slds-theme_error">
<p>Review the errors on this page.</p>
</div>
<div class="slds-m-left_large">
<p style="color: red;">
Select at least one field to display.
</p>
</div>
</template>
<div class="slds-m-left_medium">
<lightning-dual-listbox name="languages"
label="Select Fields"
source-label="Available Fields"
selected-label="Visible Fields"
field-level-help="Select the fields to display"
options={statusOptions}
value ={selectedValues}
onchange={handleChange}></lightning-dual-listbox>
</div>
</div>
<!-- Modal/Popup Box LWC footer starts here -->
<footer class="slds-modal__footer">
<button class="slds-button slds-button_neutral" onclick={closeModal} title="Cancel">Cancel</button>
<button class="slds-button slds-button_brand" onclick={handleSaveButtonClick} title="Save">Save</button>
</footer>
</div>
</section>
<div class="slds-backdrop slds-backdrop_open"></div>
</template>
<br />
</template>
import { LightningElement, api } from 'lwc';
import retreieveFields from '@salesforce/apex/dynamicDataTableController.retreieveFields';
import updateFields from '@salesforce/apex/dynamicDataTableController.updateSelectedFields';
export default class ChildDatatable extends LightningElement {
@api fecthObjectName; // holds object Name passed from parent component
fieldItems = []; // all fields map of field API and field Label
_selected = []; // all fields selected
selectedfieldItems = [];
isModalOpen = false;
isError = false;
selectedfieldMap ;
isRendered;
fieldAPINames;
connectedCallback() {
// function to fetch all fields map field API and field Label
retreieveFields({
ObjectName: this.fecthObjectName
})
.then(data => {
console.log('wrapper data =>' + data);
let result = JSON.parse(data);
this.selectedfieldMap = result.selectedFieldsMap;
let fieldAPILabelMap = result.allAvailableFieldsMap;
console.log(' wrapper data.selectedfieldMap => ' + JSON.stringify( this.selectedfieldMap));
console.log(' wrapper result.fieldAPILabelMap => ' + JSON.stringify(fieldAPILabelMap));
for (var key in fieldAPILabelMap) {
if (fieldAPILabelMap.hasOwnProperty(key)) {
this.fieldItems.push({ value: fieldAPILabelMap[key], label: key });
}
}
if(this.selectedfieldMap){
for (var key in this.selectedfieldMap) {
if (this.selectedfieldMap.hasOwnProperty(key)) {
this.selectedfieldItems.push(this.selectedfieldMap[key]);
}
}
this._selected = [... this.selectedfieldItems];
this.handleRetreiveRecords();
}
this.error = undefined;
}).catch(error => {
this.error = error;
this.fieldItems = undefined;
})
}
handleSelectFields() {
this.isModalOpen = true;
//this._selected = [];
}
closeModal() {
this.isModalOpen = false;
this.isError = false;
}
get statusOptions() {
console.log(' this.fieldItems => ' + JSON.stringify(this.fieldItems));
return this.fieldItems;
}
get selectedValues() {
return this._selected;
}
get errorStatus() {
return this.isError;
}
handleChange(e) {
this._selected = e.detail.value;
console.log(' this._selected => ' + this._selected);
if (this._selected.length > 0) {
this.isError = false;
}
}
handleSaveButtonClick(){
if (this._selected.length > 0) {
this.updateSelectedFields();
this.handleRetreiveRecords();
}else{
this.isError = true;
}
}
updateSelectedFields() {
console.log('updateSelectedFields => ' );
let selectedFieldsValue = this._selected;
this.fieldAPINames = selectedFieldsValue.toString();
console.log('updateSelectedFields this.fecthObjectName => ' +this.fecthObjectName);
console.log('updateSelectedFields this.fieldAPINames => '+this.fieldAPINames);
updateFields({
objectName: this.fecthObjectName,
fieldAPINames: this.fieldAPINames})
.then(results => {
console.log('results => ' + results);
})
.catch(error => {
console.log('error => ' + JSON.stringify(error));
});
}
handleRetreiveRecords() {
let selectedFieldsValue = this._selected;
const fieldAPIMapSelected = [];
if (this._selected.length > 0) {
this.isModalOpen = false;
this.isError = false;
selectedFieldsValue.forEach(option => {
let currentOption = this.fieldItems.find(obj => obj.value === option);
fieldAPIMapSelected.push({
label: currentOption.label,
value: currentOption.value
});
});
console.log('selectedFieldsValue => ' + selectedFieldsValue);
const fieldAPIMapSelected2 = fieldAPIMapSelected;
console.log(' this.fieldAPIMapSelected2 => ' + JSON.stringify(fieldAPIMapSelected2));
const evtCustomEvent = new CustomEvent(
'retreive',
{
detail: { selectedFieldsValue, fieldAPIMapSelected2 }
});
this.dispatchEvent(evtCustomEvent);
//this._selected = [];
}
else {
this.isError = true;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>51.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
genericDataTableLWC.html
<template>
<template if:true={recordsInPage}>
<div class="dataTable" style="overflow: hidden;height:100%">
<div style="overflow: hidden">
<lightning-datatable
key-field="Id"
data={recordsInPage}
columns={columns}
sorted-by={sortBy}
sorted-direction={sortedDirection}
onsort={handleSortdata}
hide-checkbox-column={hideCheckBox}
errors={errors}>
</lightning-datatable>
</div>
<template if:true={showPagination}>
<div>
<lightning-layout multiple-rows class="slds-var-p-vertical_x-small slds-var-p-horizontal_small">
<lightning-layout-item size="4">
<div class="slds-float_left">
{pageNumberInfo}
</div>
</lightning-layout-item>
<lightning-layout-item size="4">
<div class="slds-align_absolute-center">
<a onclick={showFirstPage}>
<lightning-button-icon icon-name="utility:left" size="small"
alternative-text="First">
</lightning-button-icon>
</a>
<a onclick={showPreviousPage}>
<lightning-button-icon icon-name="utility:chevronleft" size="small"
alternative-text="Previous">
</lightning-button-icon>
</a>
<a onclick={showNextPage}>
<lightning-button-icon icon-name="utility:chevronright" size="small"
alternative-text="Next">
</lightning-button-icon>
</a>
<a onclick={showLastPage}>
<lightning-button-icon icon-name="utility:right" size="small"
alternative-text="Next">
</lightning-button-icon>
</a>
</div>
</lightning-layout-item>
<lightning-layout-item size="4">
<div class="slds-float_right">
<span>
{recordsInfo}
</span>
</div>
</lightning-layout-item>
</lightning-layout>
</div>
</template>
</div>
</template>
<template if:false={recordsInPage}>
<h4> No Record Found for given criteria.</h4>
</template>
</template>
import { LightningElement, api, track } from 'lwc';
export default class GenericDataTableLWC extends LightningElement {
@api records = [];
@api sortable = false;
@track _selectedRows = [];
@api showPagination = false;
@api paginationSize = 10;
@api hideCheckBox = false;
@api showRowNumber = false;
@track _pageSize = 10;
@track sortedDirection = 'asc';
@track draftValues = [];
_startFromIndex = 0;
_paginationInfo = {
currentPage: 0,
totalPages: 0
};
pageSize;
@track _columns = [];
@track recordSelected = false;
@track _hideCheckBox = false;
@api _records = [];
@track recordsInPage = [];
@track byPass = false;
connectedCallback() {
this.doinit();
this.showFirstPage();
}
doinit = () => {
if (this.paginationSize == undefined || this.paginationSize !== 10) {
this.pageSize = this.paginationSize;
}
this._paginationInfo.totalPages = (((this.records.length / this.pageSize) - ((this.records.length % this.pageSize) / this.pageSize)) + (((this.records.length % this.pageSize) === 0) ? 0 : 1));
this.fetchRecordFromRecords();
}
fetchRecordFromRecords = () => {
if (this.records != undefined && this.columns != undefined) {
this.recordsInPage = this.records;
this._records = this.records;
}
}
// invoked when column is changed
@api
get columns() {
return this._columns;
}
set columns(value) {
this._columns = value;
}
// invoked when selectedRows is changed
@api
get selectedRows() {
return this._selectedRows;
}
set selectedRows(value) {
if (this.byPass == false) {
this._selectedRows = value;
}
}
// invoked when page size is changed
@api
get pageSize() {
if (!this.isNotBlank(this._pageSize)) this._pageSize = 10;
return parseInt(this._pageSize, 10);
}
/**
* @param {number} value
*/
set pageSize(value) {
this._pageSize = value;
}
isNotBlank = (checkString) => {
return (checkString !== '' && checkString !== null && checkString !== undefined);
}
get pageNumberInfo() {
if (this._records && this._records.length > 0) {
this._paginationInfo.currentPage = (((this._startFromIndex + 1) / this.pageSize) - (((this._startFromIndex + 1) % this.pageSize) / this.pageSize) + ((((this._startFromIndex + 1) % this.pageSize) === 0) ? 0 : 1));
return 'Page ' + this._paginationInfo.currentPage + ' of ' + this._paginationInfo.totalPages;
}
return 'Page 0 of 0';
}
//PAGINATION - INVOKED WHEN PAGE SIZE IS CHANGED
paginationRefresh = () => {
this._startFromIndex = 0;
}
//PAGINATION - SHOW First PAGE
showFirstPage = () => {
this.paginationRefresh();
this.processPagination();
}
//PAGINATION - SHOW PREVIOUS PAGE
showPreviousPage() {
if (this._startFromIndex > 0) {
this._startFromIndex = this._startFromIndex - this.pageSize;
this.processPagination();
}
}
//PAGINATION - SHOW NEXT PAGE
showNextPage() {
if (this._startFromIndex + this.pageSize < this._records.length) {
this._startFromIndex = this._startFromIndex + this.pageSize;
this.processPagination();
}
}
//PAGINATION - SHOW LAST PAGE
showLastPage = () => {
let result = this._records.length % this.pageSize;
if (this._startFromIndex >= 0) {
if (result === 0) {
this._startFromIndex = this._records.length - this.pageSize;
this.processPagination();
} else {
this._startFromIndex = this._records.length - result;
this.processPagination(true, -result);
}
}
}
// paginate the records
processPagination(lastSetOfRecords = null, lastNumberOfRecords = null) {
if (lastSetOfRecords) {
this.recordsInPage = this._records.slice(lastNumberOfRecords);
} else {
this.recordsInPage = this._records.slice(this._startFromIndex, this.pageSize + this._startFromIndex);
}
}
@api updateColumns(columns) {
this.columns = [...columns];
}
@api refreshTable(result) {
if (result.length === 0) {
this.records = result;
}
this.refresh();
if (result.length === 0) {
console.log('Page length 0');
this._paginationInfo = {
currentPage: 0,
totalPages: 0
};
}
}
refresh() {
this.doinit();
this.showFirstPage();
}
@api getSelected() {
var el = this.template.querySelector('lightning-datatable');
return el.getSelectedRows();
}
//Pagination Information in datatable footer
get recordsInfo() {
if (this._records.length > 0) {
this._endIndex = this._startFromIndex + this.pageSize;
return 'Showing ' + (this._startFromIndex + 1) + " to " + ((this._endIndex > this._records.length) ? this._records.length : this._endIndex) + " of " + this._records.length + " records";
}
return 'Showing 0 of 0';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>54.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
displayDatatable.html
<template>
<lightning-layout>
<div class="c-container">
<lightning-layout multiple-rows="false">
<lightning-layout-item padding="around-small">
<c-child-datatable
fecth-object-name={objectNameDatatable}
onretreive={retriveRecordHandler}>
</c-child-datatable>
</lightning-layout-item>
</lightning-layout>
</div>
</lightning-layout>
<template if:true={isRecordsVisible}>
<lightning-card>
<p class="slds-p-horizontal_medium">{objectNameDatatable} Records Using dynamic DataTable</p>
<br></br>
<c-generic-data-table-l-w-c
columns={columns}
records={data}
sortable= true
show-pagination=true
pagination-size="10"
hide-check-box="true">
</c-generic-data-table-l-w-c>
</lightning-card>
</template>
</template>
displayDatatable.js
import { LightningElement, api } from 'lwc';
import retreieveRecords from '@salesforce/apex/dynamicDataTableController.retreieveRecords';
const sfdcBaseURL = window.location.origin;
export default class DisplayDatatable extends LightningElement {
@api objectNameDatatable; // holding objectName value which is passed from App builder property
@api fieldAPINames = ''; // holds list of fields API Name for the given object
@api customSettingName; // Custom Setting which will store all the fields selected
tempColumn = []; //temporary column list to store columns
data = []; // data to display on datatable
columns; //final columns/headers of datatable
newobj = {};
newDataArray = [];
defaultSortDirection = 'asc';
isRecordsVisible = false;
retObj = {};
retriveRecordHandler(event) {
this.resetDataTable();
let args = JSON.parse(JSON.stringify(event.detail));
this.fieldAPINames = args.selectedFieldsValue.toString();
args.fieldAPIMapSelected2.forEach(itemVal => {
if (itemVal.value.includes(".Name") && itemVal.value !== 'RecordType.Name') {
this.tempColumn = [...this.tempColumn,
{
label: itemVal.label,
fieldName: itemVal.value.replace(/\.+Name/g, 'URL'),
type: 'url',
typeAttributes: {
label: {
fieldName: itemVal.value.replace(/\./g, '')
},
target: '_blank'
},
sortable: true
}];
} else {
this.tempColumn = [...this.tempColumn,
{
label: itemVal.label,
fieldName: itemVal.value.includes(".Name") ? itemVal.value.replace(/\./g, '') : itemVal.value,
sortable: true
}];
}
});
this.columns = this.tempColumn;
this.retreieveRecordsfromApex();
}
retreieveRecordsfromApex() {
console.log(' this.objectNameDatatable =>' + this.objectNameDatatable);
console.log(' this.fieldAPINames =>' + this.fieldAPINames);
retreieveRecords({
objectName: this.objectNameDatatable,
fieldAPINames: this.fieldAPINames
})
.then(data => {
console.log(' data =>' + data);
data.forEach(itemVal => {
this.retObj = this.iterate(itemVal);
if (Object.keys(this.retObj).length !== 0 && this.retObj.constructor === Object) {
this.newDataArray = [...this.newDataArray, this.retObj];
}
});
console.log(' this.newDataArray =>' + JSON.stringify(this.newDataArray));
this.data = [...this.newDataArray];
console.log(' this.data =>' + JSON.stringify(this.data) );
if (this.data) {
console.log(' inside if(this.data) ');
this.isRecordsVisible = true;
setTimeout(() => {
this.updateGenericDatatable(this.data, this.columns);
}, 100);
}
this.error = undefined;
}).catch(error => {
this.error = error;
this.data = undefined;
})
}
updateGenericDatatable(tablerecords,tablecolumns) {
console.log('this.tablecolumns ' + JSON.stringify(tablecolumns));
console.log('this.tablerecords ' + JSON.stringify(tablerecords));
if (tablecolumns !== undefined) {
this.updateColumns(tablecolumns);
console.log('tablecolumns ' + tablecolumns);
}
if (tablerecords !== undefined) {
this.updateRecords(tablerecords);
console.log('tablerecords ' + tablerecords);
}
}
updateColumns(columns) {
if (this.template.querySelector('c-generic-data-table-l-w-c')) {
this.template.querySelector('c-generic-data-table-l-w-c').updateColumns(columns);
}
}
updateRecords(records) {
if (this.template.querySelector('c-generic-data-table-l-w-c')) {
this.template.querySelector('c-generic-data-table-l-w-c').refreshTable(records);
}
}
iterate(obj) {
this.newobj = {}; // reset newobj
for (var property in obj) {
// this if blocks check if the object has any property
if (obj.hasOwnProperty(property)) {
let xmlString = obj[property];
// if block to all Reference field as URL clickable
if (typeof xmlString === "object") {
if (property !== 'RecordType') {
this.newobj[property + 'URL'] = sfdcBaseURL + '/' + obj[property].Id;
}
this.newobj[property + 'Name'] = obj[property].Name;
}
// if block to make all Formula fields URL clickable
else if (typeof xmlString === "string" && xmlString.includes('href')) {
let doc = new DOMParser().parseFromString(xmlString, "text/xml");
let PATTERN_FOR_EXTERNAL_URLS = /^(\w+:)?\/\//;
let href = doc.querySelector('a').getAttribute('href');
//alert("href.search(PATTERN_FOR_EXTERNAL_URLS)" +href.search(PATTERN_FOR_EXTERNAL_URLS));
if (href !== undefined && href.search(PATTERN_FOR_EXTERNAL_URLS) !== -1 || href.includes('www'))
this.newobj[property + 'URL'] = href;
else {
this.newobj[property + 'URL'] = sfdcBaseURL + href;
}
this.newobj[property + 'Name'] = doc.firstChild.innerHTML;
}
// this if block is to all all other fields values/property and to stop adding the RecordId to the final object
else if (property !== 'Id') {
this.newobj[property] = obj[property];
}
}//if
}//for loop
return this.newobj;
}// iterate fnc
// to reset the DataTable
resetDataTable() {
this.tempColumn = [];
this.data = [];
this.columns = [];
this.newobj = {};
this.newDataArray = [];
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>51.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordPage,lightning__AppPage,lightning__HomePage">
<property name="objectNameDatatable" label="Object API Name to Retrieve" type="String" default=""/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
- Account
- Contact
- Student__c (Custom Object)
1. Go to Set up
2. In quick find search Lightning App Builder and click.
3. Select the page you want to add the component in and Click Edit.
4. Search the component on the left search panel and then drag & drop the component.
5. Click Save and go back to the page.
Full code:- GitHub Link
Resources:
Salesforce Components Library
Thank you all !
#Keep learning #Keep Sharing :)
Recent blogs:
Custom Toast Message in LWC
Generic Modal Box in LWC
How to use hyperlink in custom Toast Notification message in LWC
Nested Accordion in LWC
Build custom add to favorites functionality using LWC
Comments
Post a Comment