In this blog, I am going to share a reusable component which can be used to display hierarchy of records along with other fields information in treegrid format.
Previously we have to use jquery to implement this but now "Lightning:treegrid" tag have been introduced which can be used to display treegrid in lightning framework.
In order to use this component, you have to pass below parameters to this components:
Previously we have to use jquery to implement this but now "Lightning:treegrid" tag have been introduced which can be used to display treegrid in lightning framework.
In order to use this component, you have to pass below parameters to this components:
- ltngcurrentRecId = RecordId (15 or 18 digit)
- ltngSobjectName = Object API Name
- ltngParentFieldAPIName = Parent Field API name which create self relationship
- ltngColumnLabelList = Specify the column label
- ltngColumnAPINameList = Specify the API field names in same order as that of column
- ltngHyperlinkColumn = Field API Name which will work as hyperlink for record detail page
Suppose you have display treegrid for Account hierarchy by using below syntax:
<c:SK_GenericTreeGridCmp ltngcurrentRecId="0019000000ld4kS"
ltngSobjectName="Account"
ltngParentFieldAPIName="ParentId"
ltngColumnLabelList="['Name','Type','Industry','Account Owner']"
ltngColumnAPINameList="['Name','Type','Industry','Owner.Name']"
ltngHyperlinkColumn="Name"/>
Below is output:
Suppose you have to display case hierarchy, then use below code snippet:
<c:SK_GenericTreeGridCmp ltngcurrentRecId="5009000000GJkJE"
ltngSobjectName="Case"
ltngParentFieldAPIName="ParentId"
ltngColumnLabelList="['CaseNumber','Subject','Status','Case Owner']"
ltngColumnAPINameList="['CaseNumber','Subject','Status','Owner.Name']"
ltngHyperlinkColumn="CaseNumber"
ltngHeaderValue="Case Hierarchy"/>
Below is output snapshot
Below is complete code snippet for your reference:
<c:SK_GenericTreeGridCmp ltngcurrentRecId="0019000000ld4kS"
ltngSobjectName="Account"
ltngParentFieldAPIName="ParentId"
ltngColumnLabelList="['Name','Type','Industry','Account Owner']"
ltngColumnAPINameList="['Name','Type','Industry','Owner.Name']"
ltngHyperlinkColumn="Name"/>
Below is output:
Suppose you have to display case hierarchy, then use below code snippet:
<c:SK_GenericTreeGridCmp ltngcurrentRecId="5009000000GJkJE"
ltngSobjectName="Case"
ltngParentFieldAPIName="ParentId"
ltngColumnLabelList="['CaseNumber','Subject','Status','Case Owner']"
ltngColumnAPINameList="['CaseNumber','Subject','Status','Owner.Name']"
ltngHyperlinkColumn="CaseNumber"
ltngHeaderValue="Case Hierarchy"/>
Below is output snapshot
Below is complete code snippet for your reference:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<aura:component controller="SK_GenericTreeGridController"> | |
<aura:attribute name="ltngcurrentRecId" type="String" required="true"/> | |
<aura:attribute name="ltngSobjectName" type="String" required="true"/> | |
<aura:attribute name="ltngParentFieldAPIName" type="String" required="true"/> | |
<aura:attribute name="ltngColumnLabelList" type="List" required="true" | |
description="provide comma seperated values"/> | |
<aura:attribute name="ltngColumnAPINameList" type="List" required="true" | |
description="provide comma seperated values"/> | |
<aura:attribute name="ltngHyperlinkColumn" type="String"/> | |
<aura:attribute name="items" type="Object"/> | |
<aura:attribute name="gridColumns" type="list" /> | |
<aura:attribute name="gridData" type="Object" /> | |
<aura:attribute name="gridExpandedRows" type="List" access="PRIVATE" /> | |
<aura:attribute name="ltngHeaderValue" type="string" default="Hierarchy Using Lightning Tree Grid"/> | |
<aura:handler name="init" value="{!this}" action="{!c.doInit}" /> | |
<div class="slds-grid slds-wrap" style="background-color:#E6E6FA;border-style: solid;margin:2%;padding:2%;"> | |
<div class="slds-col slds-size_1-of-1" > | |
<b>{!v.ltngHeaderValue}</b><br/> | |
<lightning:treeGrid columns="{! v.gridColumns }" | |
data="{! v.gridData }" | |
keyField="Id" | |
expandedRows="{! v.gridExpandedRows}" | |
aura:id="mytree" /> | |
</div> | |
</div> | |
</aura:component> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
({ | |
doInit : function(component, event, helper) { | |
var action= component.get("c.findHierarchyData"); | |
var parentFieldName = component.get("v.ltngParentFieldAPIName"); | |
var fieldList = component.get("v.ltngColumnAPINameList"); | |
var params={ | |
"recId" :component.get("v.ltngcurrentRecId"), | |
"parentFieldAPIName":component.get("v.ltngParentFieldAPIName"), | |
"objectAPIname" : component.get("v.ltngSobjectName"), | |
"columnLabelList" :component.get("v.ltngColumnLabelList"), | |
"fieldAPINameList" :component.get("v.ltngColumnAPINameList"), | |
"hyperlinkColumn" :component.get("v.ltngHyperlinkColumn") | |
}; | |
if(params){ | |
action.setParams(params); | |
} | |
//console.log('****param to controller:'+JSON.stringify(params)); | |
action.setCallback(this, function(response) { | |
var state = response.getState(); | |
if (state === "SUCCESS") { | |
var apexResponse = response.getReturnValue(); | |
//console.log(JSON.stringify(apexResponse)); | |
var columns = apexResponse.headerList; | |
console.log('***columns:'+JSON.stringify(columns)); | |
component.set('v.gridColumns', columns); | |
var expandedRows = []; | |
var hierarchydata = apexResponse.recordList; | |
var roles = {}; | |
//console.log('*******hierarchydata:'+JSON.stringify(hierarchydata)); | |
var results = hierarchydata; | |
roles[undefined] = { Name: "Root", _children: [] }; | |
hierarchydata.forEach(function(v) { | |
expandedRows.push(v.Id); | |
var recordDetail = {}; | |
fieldList.forEach(function(fieldAPIName) { | |
if(fieldAPIName.includes(".")){ | |
var fname= fieldAPIName; | |
var ss= fname.split("."); | |
//console.log('****ss.length:'+ss.length); | |
var tempValue=v[ss[0]]; | |
//console.log('****initial tempValue:'+JSON.stringify(tempValue)); | |
for(var i=1;i<ss.length;i++){ | |
console.log('****tempValue for '+fname+':'+tempValue[ss[i]]); | |
tempValue = tempValue[ss[i]]; | |
} | |
recordDetail[fname]=tempValue; | |
}else{ | |
recordDetail[fieldAPIName]=v[fieldAPIName]; | |
} | |
}); | |
recordDetail["Id"]=v["Id"]; | |
recordDetail["RecordURL"]= '/'+v["Id"]; | |
recordDetail["_children"]= []; | |
console.log('****recordDetail:'+JSON.stringify(recordDetail)); | |
roles[v.Id] = recordDetail; | |
}); | |
hierarchydata.forEach(function(v) { | |
roles[v[parentFieldName]]._children.push(roles[v["Id"]]); | |
}); | |
component.set("v.gridData", roles[undefined]._children); | |
//console.log('*******treegrid data:'+JSON.stringify(roles[undefined]._children)); | |
component.set('v.gridExpandedRows', expandedRows); | |
}else if(state === "ERROR"){ | |
var errors = response.getError(); | |
console.error(errors); | |
alert('Problem with connection.'+errors); | |
} | |
}); | |
$A.enqueueAction(action); | |
} | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Author- Sunil02kumar@gmail.com | |
public class SK_GenericTreeGridController { | |
@AuraEnabled | |
public static dataWrapper findHierarchyData(string recId,string parentFieldAPIName, | |
string objectAPIname,List<string> columnLabelList, | |
List<string> fieldAPINameList,string hyperlinkColumn){ | |
dataWrapper returnData = new dataWrapper(); | |
try{ | |
if(fieldAPINameList.size()>0 && columnLabelList.size()>0 && columnLabelList.size()==fieldAPINameList.size()){ | |
integer indexCount=0; | |
List<columnsHeaderWrapper> headerdata = new List<columnsHeaderWrapper>(); | |
for(string ss : fieldAPINameList){ | |
if(ss != null && ss != ''){ | |
columnsHeaderWrapper headerinfo= new columnsHeaderWrapper(); | |
if(ss.equalsignorecase(hyperlinkColumn)){ | |
headerinfo.type='url'; | |
headerinfo.fieldName='RecordURL'; | |
headerInfo.label=columnLabelList[indexCount]; | |
headerInfo.typeAttributes.label.fieldName=ss; | |
}else{ | |
headerinfo.type='text'; | |
headerinfo.fieldName=ss; | |
headerInfo.label=columnLabelList[indexCount]; | |
} | |
headerdata.add(headerInfo); | |
} | |
indexCount++; | |
} | |
returnData.headerList =headerdata; | |
} | |
string queryString = 'select id,ParentId ,'+string.join(fieldAPINameList,',')+' from '+objectAPIname; | |
//Section to get all child account details from ultimate parent starts------------------------- | |
List<String> currentParent = new List<String>{}; | |
Integer level = 0; | |
Boolean endOfStructure = false; | |
//method to find ultimate parent of account | |
string topMostparent = GetUltimateParentId(recId,objectAPIname,parentFieldAPIName ); | |
system.debug('*******topMostparent:'+topMostparent); | |
currentParent.add(topMostparent); | |
system.debug('**********topMostparent:'+ currentParent); | |
//Loop though all children | |
string finalQueryString = ''; | |
List<sObject> queryOutput = new List<sObject> (); | |
while ( !endOfStructure ){ | |
if( level == 0 ){ | |
finalQueryString = queryString + ' where id IN : CurrentParent ORDER BY '+parentFieldAPIName+' Limit 1000'; | |
} | |
else { | |
finalQueryString = queryString + ' where '+parentFieldAPIName+' IN : CurrentParent ORDER BY '+parentFieldAPIName+' Limit 1000'; | |
} | |
system.debug('********finalQueryString:'+finalQueryString); | |
if(finalQueryString != null && finalQueryString !=''){ | |
try{ | |
if(Limits.getLimitQueries()-Limits.getQueries()>0){ | |
queryOutput = database.query(finalQueryString); | |
system.debug('***hierarchy level:'+level); | |
}else{ | |
system.debug('****endOfStructure is true as SOQL limit reaches:'); | |
endOfStructure = true; | |
} | |
}catch(exception ex){ | |
endOfStructure = true; | |
} | |
} | |
system.debug('**queryOutput size:'+queryOutput); | |
if( queryOutput.size() == 0 ){ | |
endOfStructure = true; | |
} | |
else{ | |
currentParent.clear(); | |
//iterating through query output | |
for ( Integer i = 0 ; i < queryOutput.size(); i++ ){ | |
string recordId= string.valueof(queryOutput[i].get('Id')); | |
currentParent.add(recordId); | |
returnData.recordList.add(queryOutput[i]); | |
} | |
} | |
level++; | |
} | |
system.debug('**********returnData.recordList:'+returnData.recordList); | |
system.debug('**********returnData.headerList:'+returnData.headerList); | |
}catch(exception ex){ | |
system.debug('***exception:'+ex.getMessage()); | |
} | |
return returnData; | |
} | |
// Find the tom most element in Heirarchy | |
public static String GetUltimateParentId( string recId, string sObjectName, string parentFieldAPIname ){ | |
Boolean top = false; | |
while ( !top ) { | |
string queryString = 'select id , ' +parentFieldAPIname+ ' from '+sObjectName + ' where Id =:recId LIMIT 1'; | |
system.debug('**********queryString GetUltimateParentId:'+queryString); | |
sobject sb = database.query(queryString); | |
if ( sb.get(parentFieldAPIname) != null ) { | |
recId = string.valueof(sb.get(parentFieldAPIname)); | |
}else { | |
top = true; | |
} | |
} | |
return recId ; | |
} | |
public class dataWrapper{ | |
@AuraEnabled | |
public List<columnsHeaderWrapper> headerList; | |
@AuraEnabled | |
public List<sObject> recordList; | |
public dataWrapper(){ | |
recordList=new List<sObject>(); | |
headerList=new List<columnsHeaderWrapper>(); | |
} | |
} | |
public class columnsHeaderWrapper{ | |
@AuraEnabled | |
public string type; | |
@AuraEnabled | |
public string fieldName; | |
@AuraEnabled | |
public string label; | |
@AuraEnabled | |
public TypeAttributes typeAttributes; | |
public columnsHeaderWrapper(){ | |
typeAttributes=new TypeAttributes(); | |
} | |
} | |
public class TypeAttributes { | |
@AuraEnabled | |
public Label label; | |
public TypeAttributes(){ | |
label=new Label(); | |
} | |
} | |
public class Label { | |
@AuraEnabled | |
public String fieldName ; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<aura:application extends="force:slds"> | |
<c:SK_GenericTreeGridCmp ltngcurrentRecId="0019000000ld4kS" | |
ltngSobjectName="Account" | |
ltngParentFieldAPIName="ParentId" | |
ltngColumnLabelList="['Name','Type','Industry','Account Owner']" | |
ltngColumnAPINameList="['Name','Type','Industry','Owner.Name']" | |
ltngHyperlinkColumn="Name"/> | |
<c:SK_GenericTreeGridCmp ltngcurrentRecId="5009000000GJkJE" | |
ltngSobjectName="Case" | |
ltngParentFieldAPIName="ParentId" | |
ltngColumnLabelList="['CaseNumber','Subject','Status','Case Owner','Case Owner Email','Account Owner']" | |
ltngColumnAPINameList="['CaseNumber','Subject','Status','Owner.Name','Owner.Email','Account.Owner.Name']" | |
ltngHyperlinkColumn="CaseNumber" | |
ltngHeaderValue="Case Hierarchy"/> | |
</aura:application> |
You can download the code from GitHub by using below URL:
Lightning-Treegrid-Generic-Component-to-display-hierarchy-in-tabular-format-for-any-sobject
Note:
Note:
- Specify Field API properly as javascript is case sensitive. For example, specify "Name" instead of "name"
- For adding parent field API names, provide API names properly. For example for Account owner use, "Owner.Name" instead of "owner.name".
Hope this will help!!!
This is an awesome post. Really very informative and creative. This sharing concept is a good way to enhance the knowledge. Thank you very much for this post. I like this site very much. I like it and it help me to development very well...
ReplyDeleteMobile App Development Company In Chennai
Android App Development Company In Chennai
Android Application Development Company In Chennai
Custom Web Application Development Company In Chennai
Awesome Post.....
ReplyDeleteHi, Is there any way we can select child rows on selecting parent row? For example, We have a requirement where once user select parent account, it should select all contacts specific to that account.
ReplyDeletePlease ping if u got any solution regarding the same
DeleteRefer below link
Deletehttps://www.infallibletechie.com/2020/05/lightningtreegrid-select-child-rows.html
Great Blog... The information you shared is very effective for learners I have got some important suggestions from it, Keep Sharing such a nice blog.
ReplyDeletecustom web application development company
Is it possible to NOT show toggle icon when there are no child records for a parent record in tree grid
ReplyDeleteCould you share this is an LWC format?
ReplyDelete