Dynamic Reusable Lightning Table Component

Dynamic Reusable Lightning Table Component

Don’t Repeat Yourself (DRY) is a principle of software development aimed at reducing repetition of software development patterns. Most of the time we will find creating same kind of Component for different object over and over again. To over come this we have created Dynamic reusable lightning component.

Github Link:

https://github.com/gokulrajanoff/dynamic-table-

This component is for displaying the record in a table format, that allow sorting on header fields (without using any extra libraries) and the table is having fixed header and scrolling enabled. This component runs on standard as well as custom object.

To DO :

  1. Create Apex class :(Name It as you Like) -> Copy Paste entire Apex code
  2. Create Lightning Component -> Copy Paste entire component code , don’t forget to change the controller name to your apex , otherwise I am not responsible for the error.
  3. Controller will be automatically calculated, so no problem ,Copy Paste controller.js code , change the action,setparam() -> according to  your object and field to display.
  4. Preview as simple as that.

Overview of this Component:

Apex Class:

Table Class

Method:

Get_Record_For_Table_Input_From_Query(String objectName,String queryFields,String fullQuery)

Return:

Object that can be displayed on Table

objectName

= Name of the Object.

queryFields

= Fields we want to display.

fullQuery

= Full Soql Query.

Call this method and this will return data necessary for the table to be displayed.

Dynamic

Code :

Apex Class :

[sourcecode language=”java” wraplines=”false” collapse=”false”]
public class DynamicTableClass {

@AuraEnabled
public static object Get_Record_For_Table_Input_From_Query(String objectName,String queryFields,String fullQuery)
{
Integer headerindex=0;
TargetTable TargetTable_inst = new TargetTable();
List allListOfRecords = new List();
String Query=fullQuery;
//split all the fields into a list of strings
List fieldList= queryFields.split(‘,’);
//Creating a Dynamic Object Type with Custom object name
String listType = ‘List’;
//Creating a Dynamic Record Type with Custom object name
String EachRecordType = ”+objectName+”;
System.debug(‘listType’+listType);
System.debug(‘EachRecordType’+EachRecordType);
//Creating a generic Sobject to hold data of custom object
List ListOfRecords = (List)Type.forName(listType).newInstance();
SObject IndividualMember = (SObject)Type.forName(EachRecordType).newInstance();
//Execute the Query to get and store the data in Generic object
ListOfRecords = Database.query(Query);
system.debug(ListOfRecords);
//object field map
Map objectFields = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap();
for(Integer i=0;i<ListOfRecords.size();i++)
{
IndividualMember = ListOfRecords[i];
EachRecord Each_record_nested_List_Of_Fields = new EachRecord();
List temp = new List();
for(String field :fieldList)
{ //record id
if(field==’id’)
{
Each_record_nested_List_Of_Fields.recordId=(Id)IndividualMember.get(field);
}
//if there is any relation like account.name
if(field.containsAny(‘.’))
{
List relation = field.split(‘.’);
//get the actual value not id in relation
temp.add(IndividualMember.getSobject(relation[0]).get(relation[1]));
}
else
{ //except is store all field and values
if(field!=’id’){
temp.add((IndividualMember.get(field)==null)?”:IndividualMember.get(field));
}
}
}
System.debug(‘i=’+i+’-‘+temp);
Each_record_nested_List_Of_Fields.recordValue=temp;
allListOfRecords.add(Each_record_nested_List_Of_Fields);
}

//prepare header
List

headerList = new List
();for(String field :fieldList)
{
if(field!=’id’){
header h = new header();
h.index=headerindex++;
String s = field;
//removing __c
Integer i=s.lastIndexOf(‘_’);
if(i!=-1)
{
s=s.substring(0,i);
}
//replacing _ with space so we get account_name = account name
s=s.replaceAll(‘_’,’ ‘);
system.debug(‘String’+s);
h.name=s;
h.sorted=2;
headerList.add(h);
}
}
//system.debug(‘Header:’+headerList);
TargetTable_inst.headerList=headerList;
TargetTable_inst.records=ListOfRecords;
TargetTable_inst.ListOfEachRecord=allListOfRecords;
return TargetTable_inst;}
public class header{
@AuraEnabled public String name; //Name of the Field
@AuraEnabled public String api; //Api of the Field
@AuraEnabled public Integer sorted; //Is the FieldSorted
@AuraEnabled public Integer index; //Index of the Field on record
}
public class EachRecord{
@AuraEnabled public Map fieldValuePair; //The field and Its corresponding Value
@AuraEnabled public List recordValue; //Record
@AuraEnabled public Id recordId; //Record Id used for Firing Events
}
public class TargetTable{
@AuraEnabled public List
headerList; //Header wrapper
@AuraEnabled public List records; //actual list of records
@AuraEnabled public List ListOfEachRecord; //curated wrapped list of record facilitated for table
}
}
[/sourcecode]
Lightning Component.cmp:

[sourcecode language=”xml” wraplines=”false” collapse=”false”]
<aura:component controller=”DynamicTableClass” implements=”force:appHostable, flexipage:availableForAllPageTypes, flexipage:availableForRecordHome, force:hasRecordId, forceCommunity:availableForAllPageTypes, force:lightningQuickAction” access=”global” >
<aura:attribute name=”Table_header_Records” type=”object”/>
<aura:attribute name=”Error” type=”boolean” default=”false” description=”use for displaying error in query or backend exception”/>
<aura:attribute name=”Message” type=”boolean” default=”false” description=”use for display no record found message”/>
<aura:handler name=”init” value=”{!this}” action=”{!c.getRecordsForTable}”/>
<aura:attribute name=”showHeaders” type=”Boolean” default=”false”/>
<article class=”slds-card”>
<aura:if isTrue=”{!v.Error}”>
<div class=”slds-text-color–error”>Error In Query Or Backend Exception, check With it </div>
</aura:if>
<aura:if isTrue=”{!v.Message}”>
<div class=”slds-text-color–error”> No Result Found…</div>
</aura:if>
<aura:if isTrue=”{!v.showHeaders}”>
<div class=”slds-center” style=”align:center;”>
<div class=”slds-table–header-fixed_container” style=”height:350px;”>
<div class=”slds-scrollable_y” style=”height:100%;”>
<table class=”slds-table slds-table_bordered slds-table–header-fixed”>
<thead>
<tr class=”slds-text-title–caps”>
<aura:iteration items=”{!v.Table_header_Records.headerList}” var=”h”>
<th scope=”col” onclick=”{!c.sortColumn}” data-value=”{!h.index}”>
<div class=”slds-truncate slds-cell-fixed” title=”{!h.name}”>{!h.name}
<aura:if isTrue=”{! (h.sorted == 0) }”>&nbsp; ▼ </aura:if>
<aura:if isTrue=”{! (h.sorted == 1) }”> &nbsp; ▲ </aura:if>
</div>
</th>
</aura:iteration>
</tr>
</thead>
<tbody>
<aura:iteration items=”{!v.Table_header_Records.ListOfEachRecord}” var=”tar”>
<tr onclick=”{!c.displayfields}” data-value=”{!tar.recordId}”>
<aura:iteration items=”{!tar.recordValue}” var=”value”>
<td>
<a><div class=”slds-truncate”>{!value}</div></a>
</td>
</aura:iteration>
</tr>
</aura:iteration>
</tbody>
</table>
</div></div>
</div>
</aura:if>
</article>
</aura:component>
[/sourcecode]

Lightning Controller.js:

[sourcecode language=”javascript” wraplines=”false” collapse=”false”]
({
getRecordsForTable : function(component, event, helper) {
var action=component.get(“c.displayTable”);
action.setCallback(this,function(response)
{
component.set(“v.Table_header_Records”,response.getReturnValue());
var records = component.get(“v.Table_header_Records”);
console.log(component.get(“v.Table_header_Records”));
if(records==null)
{
component.set(“v.Error”, true);
component.set(“v.showHeaders”,false);
}
if (records!=null && records.ListOfEachRecord.length == 0) {
component.set(“v.Message”, true);
component.set(“v.showHeaders”,false);

} else {
component.set(“v.Message”, false);
component.set(“v.showHeaders”,true);
}
});
$A.enqueueAction(action);
},
sortColumn : function(component, event, helper) {
var ctarget = event.currentTarget;
var Fieldindex = ctarget.dataset.value;
console.log(“selected Field” Fieldindex);
var Wrapedrecords = component.get(“v.Table_header_Records”);
var objectRecord = Wrapedrecords.ListOfEachRecord;
var headerRecord = Wrapedrecords.headerList;
console.log(headerRecord);
for(var i=0;i<headerRecord.length;i ){
console.log(“loop:” headerRecord[i].name ” – ” Fieldindex);
if(headerRecord[i].index==Fieldindex)
{ console.log(“match entered”);
var sorted = headerRecord[i].sorted;
if(sorted==1)
{
//desc
console.log(“descending”);
objectRecord.sort(function(a,b) {
return (a[‘recordValue’][headerRecord[i].index].toString().toLowerCase() < b[‘recordValue’][headerRecord[i].index].toString().toLowerCase() ) ? 1 : ((b[‘recordValue’][headerRecord[i].index].toString().toLowerCase() < a[‘recordValue’][headerRecord[i].index].toString().toLowerCase() ) ? -1 : 0);} );
sorted=0;
}
else if(sorted==0 || sorted==2)
{
console.log(“ascending”);
objectRecord.sort(function(a,b) {return (a[‘recordValue’][headerRecord[i].index].toString().toLowerCase() > b[‘recordValue’][headerRecord[i].index].toString().toLowerCase() ) ? 1 : ((b[‘recordValue’][headerRecord[i].index].toString().toLowerCase() > a[‘recordValue’][headerRecord[i].index].toString().toLowerCase() ) ? -1 : 0);} );
sorted=1;
}
headerRecord[i].sorted=sorted;

}
else{
headerRecord[i].sorted=2;
}
}
Wrapedrecords.records=objectRecord;
Wrapedrecords.headerList=headerRecord;
console.log(Wrapedrecords);
component.set(“v.Table_header_Records”,Wrapedrecords);

}

})
[/sourcecode]

Application :

[sourcecode language=”xml” wraplines=”false” collapse=”false”]
<aura:application extends=”force:slds”>
<center><h1>Table1</h1></center>
<c:testDynamicComponent ></c:testDynamicComponent>
<br/>
<center><h1>Table2</h1></center>
<c:DynamicTablecomponent ></c:DynamicTablecomponent>
</aura:application>
[/sourcecode]

Output:

Dynamic

Clicking on the Column will sort the column:

Screen

Note:

Place we need to modify code to make it run on different object

[sourcecode language=”javascript” wraplines=”false” collapse=”false”]
var action=component.get(“c.Get_Record_For_Table_Input_From_Query”);
action.setParams({“objectName”:”contact”,”queryFields”:”name,title,phone,email”,”fullQuery”:”select name,title,phone,email from contact”});
[/sourcecode]

Or we can call this method DynamicTableClass.Get_Record_For_Table_Input_From_Query(objectName, queryFields, fullQuery) from other apex class.

Similarly we can create Different component that will display records from different object by just modifying the parameter passed to Get_Record_For_Table_Input_From_Query() function.

Dynamic

Other Features:

  1. On Click of a row, a Controller function will be called , we can get the record id of that row by

[sourcecode language=”javascript” wraplines=”false” collapse=”false”]
var ctarget = event.currentTarget;
var RecordID = ctarget.dataset.value;
[/sourcecode]

we can use the Record Id to fire events or else do something as you wish :D.

2. Sorting Done Client side without extra libraries , so no headache in creating static resource fields. And its fast , no server calls.

3.Scrollable View , so we can see may records at a time , we can adjust the height of table according to your wish.

2 thoughts on “Dynamic Reusable Lightning Table Component”

  1. I am working on using this ( this is so good for many things). In your git comments you note to just go to DynamicTableapp.app, and change the query. I can’t find this, can you give some more direction on this. Do i have to still copy all the code? ( in a sandbox and then deploy?) change that code then it will work?

Leave a Comment

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

Recent Posts

what is salesforce service cloud and how can it help your business
What is Salesforce Service Cloud and how can it help your business?
salesforce manufacturing cloud integration for the seamless sales process
Salesforce Manufacturing Cloud Integration for the Seamless Sales Process
top 5 benefits of using salesforce for high tech industry infographic
Top 5 Benefits of using Salesforce for High-Tech Industry
salesforce world tour essentials dubai
Salesforce World Tour Essentials Dubai | May 16, 2024
simplifying npl the magic of natural language processing
Simplifying NLP: The Magic of Natural Language Processing
Scroll to Top