Aug 30, 2016

Dynamic nth Level Hierarchy in Salesforce

This blog is somewhat related to my previous post but with bit easy implementation and more generic. We have encountered this implementation multiple time so thought of sharing it with all.

Ever thought of replicating this feature in any of your custom object?


Now the only complication is, in case of self lookup you don't know the end node and where your loop should stop (to think about the logic). So hope I will make it easy for you.

*Note : My blog code formatter is not working properly, so please check the spaces before using the code.

Step 1 : Create an apex class called HierarchyController, as shown below and save it.

public with sharing class DynamicHierarchyForBlogController 
{
 public String objectApiName {get;set;}
 public String fieldApiName {get;set;}
 public String nameField {get;set;}
 public String currentsObjectId {get;set;}
 public String topParentId {get;set;}
 public String jsonMapsObjectString {get;set;}
 private String jsonString;
 private Map> sObjectIdMap ;
 private Map selectedsObjectMap ;
 private Map allsObjectMap;

 public DynamicHierarchyForBlogController() 
 {
  currentsObjectId = Apexpages.currentPage().getParameters().get('id');
     sObjectIdMap = new Map>();
  selectedsObjectMap = new Map();
  allsObjectMap = new Map();
 }
 
 public String getjsonMapString()
 {
  retrieveInfo();
  return jsonString;
 }
 
 public void retrieveInfo()
 {
  String dynamicQuery = 'SELECT ID ,' + fieldApiName + ' , ' + nameField + ' FROM ' + objectApiName + ' ORDER BY ' + fieldApiName + '  LIMIT 50000';
  for(sObject obj: Database.query(dynamicQuery))
  {
   allsObjectMap.put(obj.id,obj);
  }

  if(currentsObjectId != null)
  {
   String dQuery = 'SELECT ID FROM ' + objectApiName + ' WHERE id =\'' + currentsObjectId +'\'';
   List objList = Database.query(dQuery);
   currentsObjectId = objList[0].Id;
   retrieveTopParent(currentsObjectId);
   retrieveAllChildRecords(new Set{topParentId});
   for(String str : sObjectIdMap.keySet())
   {
    selectedsObjectMap.put(str,allsObjectMap.get(str));
   }
   jsonString = JSON.serialize(sObjectIdMap);
   jsonMapsObjectString = JSON.serialize(selectedsObjectMap);
  }
 }

 public void retrieveTopParent(String sObjectId)
 {
  if(allsObjectMap.keySet().contains(sObjectId) && allsObjectMap.get(sObjectId).get(fieldApiName) != null)
  {
   topParentId = String.valueOf(allsObjectMap.get(sObjectId).get(fieldApiName));
   retrieveTopParent(topParentId);
  }
 }

 public void retrieveAllChildRecords(Set sObjectIdSet)
 {
  if(sObjectIdSet.size() > 0)
  { 
   Set allChildsIdSet = new Set();
   for(String str : sObjectIdSet)
   {
    Set childsObjectIdSet = new Set();
    for(sObject obj : allsObjectMap.values())
    {
     if(obj.get(fieldApiName) != null && String.valueOf(obj.get(fieldApiName)) == str)
     {
      childsObjectIdSet.add(obj.Id);
      allChildsIdSet.add(obj.Id);
     }
    }
    sObjectIdMap.put(str,childsObjectIdSet);
   }
   retrieveAllChildRecords(allChildsIdSet);
  }
 }
}

Step 2 : Now create a component called Hierarchy, as shown below and save it.

<apex:component controller="DynamicHierarchyForBlogController">
 
 <apex:attribute name="objectName" description="Name of Object." type="String" required="true" assignTo="{!objectApiName}"/>
 <apex:attribute name="FieldName" description="Name of the field of the object." type="String" required="true" assignTo="{!fieldApiName}"/>
 <apex:attribute name="RepersenterField" description="Name field of the object." type="String" required="true" assignTo="{!nameField}"/>
 <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
 
 <div id="parentDiv"></div>
 
    <style>
     #parentDiv ul:first-child
     {
      padding: 0;
     }
     #parentDiv li
     {
      list-style: none;
      padding: 10px 5px 0 5px;
      position: relative;
     }
  #parentDiv li span 
  {
      -moz-border-radius: 5px;
      -webkit-border-radius: 5px;
      /*border: 1px solid #999;*/
      border-radius: 5px;
      display: inline-block;
      padding: 3px 8px;
      cursor: pointer;
  }
  .selectedRecord
  {
   font-weight:bold; 
   color:blue;
  }
    </style>
    <script>
      var accountMap = JSON.parse('{!jsonMapString}');
      var accountValueMap = JSON.parse('{!jsonMapsobjectString}');
      var cClass = '';
  if("{!topParentId}".indexOf('{!$CurrentPage.parameters.id}') != -1)
   cClass = 'selectedRecord';
  var ul = '<ul><li id="{!topParentId}"  ><span class="' + cClass + '"  onclick="toggleChilds(\'' + '{!topParentId}' + '\',event)" ><b id="i{!topParentId}" class="minus" style="font-size: 1.5em;" >-</b>&nbsp;' +  accountValueMap['{!topParentId}'].{!RepersenterField} + '</span></li></ul>'  ;
  $(ul).appendTo("#parentDiv");
  appendUl('{!topParentId}','{!topParentId}');
      function appendUl(key)
      {
       $.each( accountMap[key], function( index, value ) 
       {
      var dclass = '';
      if(value.indexOf('{!$CurrentPage.parameters.id}') != -1)
       dclass = 'selectedRecord';
      var ul = '<ul class="' + key + '"><li id="' + value + '" ><span class="' + dclass + '" onclick="toggleChilds(\'' + value + '\',event)" ><b id="i' + value + '" class="minus" style="font-size: 1.5em;" >-</b>&nbsp;' + accountValueMap[value].{!RepersenterField} + "</span></li></ul>"  ;
      $(ul).appendTo("#" + key);
      if(value)
       appendUl(value);
   });
      }
      function toggleChilds(key,event)
      {
       $('.'+key).toggle('slow');
       $('#i'+key).toggleClass('minus');
       $('#i'+key).toggleClass('plus');
       if($('#i'+key).hasClass("minus")) 
        $('#i'+key).html("-");
         if($('#i'+key).hasClass("plus")) 
             $('#i'+key).html("+");
       event.stopPropagation();
      }
    </script>
</apex:component>

Step 3 : Now create a visualforce page, as shown below.

<apex:page standardcontroller="Account" showHeader="true" sidebar="true">
 <apex:pageblock title="Hierarchy">
  <c:DynamicHierarchyForBlog objectName="Account" FieldName="ParentId" RepersenterField="Name"/>
 </apex:pageblock>
</apex:page>
Open the page in the browser and pass a valid account id and the output will be a collapsible hierarchy as shown below.



This can plot the hierarchy of nth level and of any object. Post as comment if you have any questions.

Happy Coding!!