In this post, I am going to share a component that I built while playing with LWC(Lightning Web Components). This is a simple LWC component with most logic handled by Apex.
While playing, I decided to create a sample tree structure which I think is useful to get the proper hierarchy for the records having self-relationship or child relationships. In this example, I want to display a complete view of all Accounts as well as a hierarchy for accounts within a single tree itself.
With LWC, I am able to make this tree structure minimum lines of code in my HTML component by using <lightning-tree></lightning-tree>. With the help of this component, it displays the related contacts, and cases for an Account. I have also created a self lookup relationship on Account which will have a hierarchy of Accounts, and this hierarchy will be displayed in the same tree structure along with other relationships.
Below is the code:
DynamicTreeStructureController.cls
/** * @description : * @author : @tandonprateek * @group : * @last modified on : 04-13-2022 * @last modified by : @tandonprateek **/ public with sharing class DynamicTreeStructureController { public DynamicTreeStructureController() { } private static Map<Id, TreeStructure> result; private static Map<Id, Id> childIdMap; /** Static Method to be fed in @wire for LWC */ @AuraEnabled(cacheable=true) public static List<TreeStructure> getAccounts(){ result = new Map<Id, TreeStructure>(); childIdMap = new Map<Id, Id>(); Map<Id, Account> accMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE ParentId = null]); if(!accMap.isEmpty()){ startFetchingAccountDetails(accMap); } System.debug(JSON.serialize(result)); return result.values(); } /** * Recursion method to get all levels of accounts and their related records */ private static List<TreeStructure> startFetchingAccountDetails(Map<Id, Account> accMap){ Map<Id, TreeStructure> parentStructure = gatherAllAccountInformation(accMap); //attach the first level to actual result and rest will auotmatically link //due to pass by reference way if(result == null || result.isEmpty()){ result.putAll(parentStructure); } Map<Id, Account> childMap = new Map<Id, Account>([SELECT Id, Name, DemoLight12__Account__c FROM Account WHERE DemoLight12__Account__c =: accMap.keySet()]); if(childMap != null && !childMap.isEmpty() && childMap.size() > 0){ Map<Id, Id> accChildIdMap = new Map<Id, Id>(); for(Id childAccountId : childMap.keySet()){ Account child = childMap.get(childAccountId); childIdMap.put(child.Id, child.DemoLight12__Account__c); } //run this method recursively to get all child levels. List<TreeStructure> childStructure = startFetchingAccountDetails(childMap); for(TreeStructure child : childStructure){ TreeStructure parent = parentStructure.get(childIdMap.get(child.name)); parent.items.add(child); } } return parentStructure.values(); } /** * Method to gather all information for all accounts recieved */ private static Map<Id, TreeStructure> gatherAllAccountInformation( Map<Id, Account> accMap){ Map<Id, TreeStructure> result = new Map<Id, TreeStructure>(); Map<Id, List<Contact>> accConMap = new Map<Id, List<Contact>>(); Map<Id, List<Opportunity>> accOppCMap = new Map<Id, List<Opportunity>>(); Map<Id, List<Case>> conCaseCMap = new Map<Id, List<Case>>(); //gather all contacts for(Contact con : [SELECT Id, Name, AccountId FROM Contact WHERE AccountId =: accMap.keySet()]){ if(!accConMap.containsKey(con.AccountId)){ accConMap.put(con.AccountId, new List<Contact>()); } accConMap.get(con.AccountId).add(con); } //gather all cases for(Case cas : [SELECT Id, CaseNumber, ContactId FROM Case WHERE ContactId =: accConMap.keySet()]){ if(!conCaseCMap.containsKey(cas.ContactId)){ conCaseCMap.put(cas.ContactId, new List<Case>()); } conCaseCMap.get(cas.ContactId).add(cas); } for(Id accountId : accMap.keySet()){ Account acc = accMap.get(accountId); TreeStructure accStructure = new TreeStructure(acc.name, accountId, false, null); //add all contacts if present if(accConMap.containsKey(accountId)){ TreeStructure conStructure = new TreeStructure('Contacts', 'Contacts', false, null); for(Contact con : accConMap.get(accountId)){ conStructure.items.add( new TreeStructure(con.Name, con.Id, false, null)); if(conCaseCMap.containsKey(con.Id)){ TreeStructure caseStructure = new TreeStructure('Cases', 'Cases', false, null); for(Case cas : conCaseCMap.get(con.Id)){ caseStructure.items.add( new TreeStructure(cas.CaseNumber, cas.Id, false, null)); } conStructure.items.add(caseStructure); } } accStructure.items.add(conStructure); } result.put(accountId, accStructure); } return result; } }
/** * @description : * @author : @tandonprateek * @group : * @last modified on : 04-13-2022 * @last modified by : @tandonprateek **/ public class TreeStructure{ @AuraEnabled public String label; @AuraEnabled public String name; @AuraEnabled public Boolean expanded; @AuraEnabled public List<TreeStructure> items; public TreeStructure(String label, String name, Boolean expanded, List<TreeStructure> items){ this.label = label; this.name = name; this.expanded = expanded; if(items != null && items.size() > 0){ this.items = items; }else{ this.items = new List<TreeStructure>(); } } }
<!-- @description : @author : @tandonprateek @group : @last modified on : 04-13-2022 @last modified by : @tandonprateek --> <template> <lightning-card title="Tree Components with mutliple nested Accounts and other child records of Accounts"> <div class="slds-m-top_medium slds-m-bottom_x-large"> <!-- Simple --> <template if:true={accounts.data}> <div class="slds-p-around_medium lgc-bg"> <lightning-tree items={accounts.data} header="Accounts"></lightning-tree> </div> </template> </div> </lightning-card> </template>
/** * @description : * @author : @tandonprateek * @group : * @last modified on : 04-13-2022 * @last modified by : @tandonprateek **/ import { LightningElement, wire } from 'lwc'; import getAccounts from '@salesforce/apex/DynamicTreeStructureController.getAccounts'; export default class Dynamic_Tree_Structure extends LightningElement { @wire(getAccounts) accounts; }
governor limits and might need to modify the logic as per the requirements.