Showing posts with label Dynamic. Show all posts
Showing posts with label Dynamic. Show all posts

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!!

Dec 22, 2012

Uploading Multiple Attachments into Salesforce - Simple Code

Okay, so here is the simple code to upload multiple attachments into Salesforce. Please note that I've used standard controller ("Account" in the code) so you need to pass the record Id in parameter. You can also change this according to your need as code is dynamic.

So first option allow you to select how many files you want to upload. Say, you have selected "5", then it will give you 5 options to select files and once you click on "Upload" it will upload the files as attachments to the associated record.



Visualforce Page Code:


<apex:page standardController="Account" extensions="MultipleUploadController">
    <apex:form>
        <apex:pageBlock title="Upload Multiple Attachment to Object">
            
            <apex:pageBlockButtons>
                <apex:commandButton value="Upload"  action="{!SaveAttachments}"/>
            </apex:pageBlockButtons>
            
            <apex:pageMessages id="MSG"/>
            <apex:actionFunction name="ChangeCount" action="{!ChangeCount}"/>
            
            <apex:pageblocksection>
                            
                <apex:pageBlockSectionItem>
                    <apex:outputLabel value="How many files you want to upload?"/>
                    <apex:selectList onchange="ChangeCount() ;" multiselect="false" size="1" value="{!FileCount}">
                        <apex:selectOption itemLabel="--None--" itemValue=""/>
                        <apex:selectOptions value="{!filesCountList}"/>
                    </apex:selectList>
                </apex:pageBlockSectionItem>
            
            </apex:pageblocksection>
            
            <apex:pageBlockSection title="Select Files" rendered="{!IF(FileCount != null && FileCount != '', true , false)}">
                <apex:repeat value="{!allFileList}" var="AFL">
                    <apex:inputfile value="{!AFL.body}" filename="{!AFL.Name}" />
                    <br/>
                </apex:repeat>
            </apex:pageBlockSection>
            
        </apex:pageBlock>
    </apex:form>
</apex:page>


Apex Code:


public class MultipleUploadController
{   
    //Picklist of tnteger values to hold file count
    public List<SelectOption> filesCountList {get; set;}
    //Selected count
    public String FileCount {get; set;}
    
    public List<Attachment> allFileList {get; set;}
    
    public MultipleUploadController(ApexPages.StandardController controller)
    {
        //Initialize  
        filesCountList = new List<SelectOption>() ;
        FileCount = '' ;
        allFileList = new List<Attachment>() ;
        
        //Adding values count list - you can change this according to your need
        for(Integer i = 1 ; i < 11 ; i++)
            filesCountList.add(new SelectOption(''+i , ''+i)) ;
    }
    
    public Pagereference SaveAttachments()
    {
        String accId = System.currentPagereference().getParameters().get('id');
        if(accId == null || accId == '')
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.ERROR,'No record is associated. Please pass record Id in parameter.'));
        if(FileCount == null || FileCount == '')
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.ERROR,'Please select how many files you want to upload.'));

        List<Attachment> listToInsert = new List<Attachment>() ;
        
        //Attachment a = new Attachment(parentId = accid, name=myfile.name, body = myfile.body);
        for(Attachment a: allFileList)
        {
            if(a.name != '' && a.name != '' && a.body != null)
                listToInsert.add(new Attachment(parentId = accId, name = a.name, body = a.body)) ;
        }
        
        //Inserting attachments
        if(listToInsert.size() > 0)
        {
            insert listToInsert ;
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.INFO, listToInsert.size() + ' file(s) are uploaded successfully'));
            FileCount = '' ;
        }
        else
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.ERROR,'Please select at-least one file'));
            
        return null;
    }
    
    public PageReference ChangeCount()
    {
        allFileList.clear() ;
        //Adding multiple attachments instance
        for(Integer i = 1 ; i <= Integer.valueOf(FileCount) ; i++)
            allFileList.add(new Attachment()) ;
        return null ;
    }
}


You can enhance the code if you like, as I've provided the basic code which is re-usable. Also you can change the object passed in standard controller and code will handle the rest. URL should have ID else you will get error on UI :-)

Say, I've saved my VFP code as "MultipleUpload", so my URL will be https://...../apex/MultipleUpload?id=XXXXXXXXXXXXXXX (account id in this code).

Happy Coding.

Nov 23, 2012

Page Block Table With Dynamic Columns In Salesforce

This is something extensively used, but every time I need to look for some easy code.

So here is the solution (apologies as I've not covered the negative cases in code, and I hope you all are smart enough to take care of that). Requirement is to build the dynamic page block table with dynamic columns, but this is not it. It should be so dynamic that user can select the object and it's fields by himself and nothing should be hard-coded. So first of all I've provided a picklist with all objects (not all objects... why? Take a look at it Please Remove The System Objects From My sObject List)

Visualforce Page Code:

<apex:page controller="DynamicTableController">
<apex:pageBlock >
    <apex:form >
        <apex:actionFunction name="ObjectFileds" action="{!ObjectFields}"/>
        
        <apex:commandButton  value="Show Table" action="{!ShowTable}"/>
        
        <apex:pageBlockSection >
            <apex:pageBlockSectionItem >
                <apex:outputLabel value="Select Object"/>
                <apex:selectList multiselect="false" size="1" value="{!SelectedObject}" onchange="ObjectFileds();">
                    <apex:selectOption itemLabel="--None--" itemValue="--None--"/>
                    <apex:selectoptions value="{!supportedObject}" />
                </apex:selectlist>
            </apex:pageBlockSectionItem>
            
            <apex:pageBlockSectionItem >
                <apex:outputLabel value="Select Field"/>
                <apex:selectList multiselect="true" size="5" value="{!SelectedFields}">
                    <apex:selectOption itemLabel="--None--" itemValue="--None--"/>
                    <apex:selectoptions value="{!fieldLableAPI}" />
                </apex:selectlist>
            </apex:pageBlockSectionItem>
            
            <apex:pageBlockTable rendered="{!IF(ObjectList.size > 0 , true , false)}" value="{!ObjectList}" var="rec">
                <apex:column value="{!rec.Id}" rendered="{!IF(SelectedFields.size == 0 , true, false)}"/>
                <apex:repeat value="{!SelectedFields}" var="FieldLable">
                    <apex:column value="{!rec[FieldLable]}" rendered="{!IF(FieldLable != '--None--' , true, false)}"/>
                </apex:repeat>
            </apex:pageBlockTable>
            
            <apex:outputPanel rendered="{!IF(ObjectList.size < 1 , true , false)}">
                <apex:pageMessage severity="ERROR" summary="No records to display"/>
            </apex:outputPanel>
            
        </apex:pageBlockSection>
        
    </apex:form>
</apex:pageBlock>
</apex:page>


Apex Code (Class):

public class DynamicTableController
{
    //List displayed on UI
    public List<selectoption> supportedObject {get; set;}
    
    //Selected Object
    public String SelectedObject {get; set;}
    
    //Global describe
    Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
    Set<String> objectKeys = gd.keySet();
    
    //Field Select List
    public List<SelectOption> fieldLableAPI {get; set;}
    
    //Selected fields to be displayed in table
    public List<String> SelectedFields {get; set;}
    
    //List to maintain dynamic query result
    public List<sObject> ObjectList {get; set;}
    
    
    //Constructor
    public DynamicTableController()
    {
        //Initialize
        supportedObject = new List<selectoption>() ;
        SelectedObject = '' ;
        fieldLableAPI = new List<SelectOption>() ;
        SelectedFields = new List<String>() ;
        ObjectList = new List<sObject>() ;
        
        //Get only reference to objects
        for(Schema.SObjectType item : ProcessInstance.TargetObjectId.getDescribe().getReferenceTo())
        {
            //Excluding custom setting objects
            if(!item.getDescribe().CustomSetting)
            {
                //Adding to list
                supportedObject.add(new SelectOption(item.getDescribe().getLocalName().toLowerCase() , item.getDescribe().getLabel() ));
            }
        }
        
    }
    
    //Get fields of selected object
    public void ObjectFields()
    {
        if(SelectedObject != '--None--')
        {
            //Creating sObject for dynamic selected object
            Schema.SObjectType systemObjectType = gd.get(SelectedObject);
            //Fetching field results
            Schema.DescribeSObjectResult r = systemObjectType.getDescribe();
                
            Map<String, Schema.SObjectField> M = r.fields.getMap();
            //Creating picklist of fields
            for(Schema.SObjectField fieldAPI : M.values())
            {
                fieldLableAPI.add(new SelectOption(fieldAPI.getDescribe().getName() , fieldAPI.getDescribe().getLabel())) ;
            }
        }
    }
    
    public void ShowTable()
    {
        //Creating dynamic query with selected field
        String myQuery = 'Select Id ' ;
        
        for(String field : SelectedFields)
        {
            if(field.toLowerCase() != 'id' && field.toLowerCase() != '--none--')
            myQuery += ','+ field + ' ' ;
        }
        
        //Limit is 100 for now you can change it according to need
        myQuery += ' from ' + SelectedObject + ' LIMIT 100' ;
        
        //Executing the query and fetching results
        ObjectList = Database.query(myQuery) ;
    }
}

Once user selects any object from the first picklist, filed picklist will be populated with all the field labels related to that object. Note, it's a multi-select picklist so user can select multiple fields. Once fields are selected and "Show Table" is clicked, page block table will display the field values (selected in field picklist) of all records (limit is 100 right now, you can change it according to your need).

There are some extra checks that needs to be implemented in code like "isQueryable" etc..

Aug 4, 2011

Inserting sObject Dynamically in Salesforce

I spend most of my time over Community, best place to learn and share knowledge. There are many questions related to sObject, and out of all a very interesting one is "How I can insert sObject record dynamically?"


I will provide a user interface with two text fields which will take "Object Name" and "Record Name" as string from user. And when I click on Save the record with Name entered should be inserted in the provided Object. Is this possible, of-course it is.


Here is the Visualforce Page code :
<apex:page controller="InsertDynamicSobjectController">
    <apex:form >
        <apex:pageBlock >
            <apex:pageBlockButtons >
                <apex:commandButton value="Save" action="{!Save}"/>
            </apex:pageBlockButtons>
            <apex:pageMessages />
            <apex:pageBlockSection >
                <apex:pageBlockSectionItem >
                    <apex:outputLabel value="Enter Object Name"/>
                    <apex:inputText value="{!ObjectName}"/>
                </apex:pageBlockSectionItem>
                <apex:pageBlockSectionItem >
                    <apex:outputLabel value="Enter Name for Record"/>
                    <apex:inputText value="{!RecordName}"/>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Here is the Apex Code :
public class InsertDynamicSobjectController
{
    public String ObjectName {get; set;}
    
    public String RecordName {get; set;}
    
    public InsertDynamicSobjectController()
    {
        ObjectName = '' ;
        RecordName = '' ;
    }
    
    public PageReference Save()
    {
        //use GlobalDecribe to get a list of all available Objects
        Map gd = Schema.getGlobalDescribe();
        Set objectKeys = gd.keySet();
        if(objectKeys.contains(Objectname.toLowerCase()))
        {
            try
            {
                //Creating a new sObject
                sObject sObj = Schema.getGlobalDescribe().get(ObjectName).newSObject() ;
                sObj.put('name' , RecordName) ;
                insert sObj ;
                
                PageReference pg = new PageReference('/'+sObj.Id) ;
                return pg ;
            }
            Catch(Exception e)
            {
                ApexPages.addMessages(e) ;
                return null ;
            }
        }
        else
        {
            ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,'Object API name is invalid')) ;
            return null ;
        }
    }
}


Code provided is flexible and you can extend it according to your need.