+ Start a Discussion
Vablet DeveloperVablet Developer 

Apex dependent picklist

The solution all over the web, http://titancronus.com/blog/2014/05/01/salesforce-acquiring-dependent-picklists-in-apex/ does not work in all cases it seems.

Is there an official solution yet, or maybe an alternative? That code above is the only one that seems to be floating around on different sites.
NagaNaga (Salesforce Developers) 
Hi Mamoru,

Please see the below code

User-added imageBest Regards
Naga Kiran
Vablet DeveloperVablet Developer
Need an Apex solution like the code linked to, however one that works 100%
ManojjenaManojjena
Hi Vablet ,

I understood your probelm ,Please try with below code it will work perfectly.however you need to do one thing each controlling field should have atleast one dependent field .

The map in code will return you the key as controlling field and vaue will be the list of dependent field .
 
Map<String,List<String>> pickValueMap=DependentPickListValueController.GetDependentOptions('SObject','ControllingPickList','DependentPicklist');

/////////////////////////////////////////////////////////////////////////////////////////////////
public class DependentPickListValueController{
    public  DependentPickListValueController(){}
    public  static Map<String,List<String>> GetDependentOptions(String pObjName, String pControllingFieldName, String pDependentFieldName){
        Map<String,List<String>> objResults = new Map<String,List<String>>();
        //get the string to sobject global map
        Map<String,Schema.SObjectType> objGlobalMap = Schema.getGlobalDescribe();
        //get the type being dealt with
        Schema.SObjectType pType = objGlobalMap.get(pObjName);
        Map<String, Schema.SObjectField> objFieldMap = pType.getDescribe().fields.getMap();
        //get the control values   
        List<Schema.PicklistEntry> ctrl_ple = objFieldMap.get(pControllingFieldName).getDescribe().getPicklistValues();
        //get the dependent values
        List<Schema.PicklistEntry> dep_ple = objFieldMap.get(pDependentFieldName).getDescribe().getPicklistValues();
        //iterate through the values and get the ones valid for the controlling field name
        PickListUtils.Bitset objBitSet = new PickListUtils.Bitset();
        //set up the results
        for(Integer pControllingIndex=0; pControllingIndex<ctrl_ple.size(); pControllingIndex++){            
            //get the pointer to the entry
            Schema.PicklistEntry ctrl_entry = ctrl_ple[pControllingIndex];
            //get the label
            String pControllingLabel = ctrl_entry.getLabel();
            //create the entry with the label
            objResults.put(pControllingLabel,new List<String>());
        }
        //check the dependent values
        for(Integer pDependentIndex=0; pDependentIndex<dep_ple.size(); pDependentIndex++){            
            //get the pointer to the dependent index
            Schema.PicklistEntry dep_entry = dep_ple[pDependentIndex];
            //get the valid for
            String pEntryStructure = JSON.serialize(dep_entry);                
            PickListUtils.PicklistDetails objDepPLE = (PickListUtils.PicklistDetails)JSON.deserialize(pEntryStructure, PickListUtils.PicklistDetails.class);
            //iterate through the controlling values
            for(Integer pControllingIndex=0; pControllingIndex<ctrl_ple.size(); pControllingIndex++){    
                if (objBitSet.fitBit(objDepPLE.validFor,pControllingIndex)){                    
                    //get the label
                    String pControllingLabel = ctrl_ple[pControllingIndex].getLabel();
                    objResults.get(pControllingLabel).add(objDepPLE.label);
                }
            }
        } 
        return objResults;
    }
}
//////////////////////////////////////////////////////////////////

public class PickListUtils{
    public PickListUtils(){}
    public class PicklistDetails{
            public string active {get;set;}
            public string defaultValue {get;set;}
            public string label {get;set;}
            public string value {get;set;}
            public string validFor {get;set;}
            public PicklistDetails(){}
    }
    public class Bitset{
        public Map<String,Integer> AlphaNumCharCodes {get;set;}
        public Map<String, Integer> Base64CharCodes { get; set; }
        public Bitset(){
            findChacterCodes();
        }
        private void findChacterCodes(){
            AlphaNumCharCodes = new Map<String,Integer>{
                'A'=>65,'B'=>66,'C'=>67,'D'=>68,'E'=>69,'F'=>70,'G'=>71,'H'=>72,'I'=>73,'J'=>74,
                'K'=>75,'L'=>76,'M'=>77,'N'=>78,'O'=>79,'P'=>80,'Q'=>81,'R'=>82,'S'=>83,'T'=>84,
                'U'=>85,'V'=> 86,'W'=>87,'X'=>88,'Y'=>89,'Z'=>90    
            };
            Base64CharCodes = new Map<String, Integer>();
            //lower case
            Set<String> pUpperCase = AlphaNumCharCodes.keySet();
            for(String pKey : pUpperCase){
                //the difference between upper case and lower case is 32
                AlphaNumCharCodes.put(pKey.toLowerCase(),AlphaNumCharCodes.get(pKey)+32);
                //Base 64 alpha starts from 0 (The ascii charcodes started from 65)
                Base64CharCodes.put(pKey,AlphaNumCharCodes.get(pKey) - 65);
                Base64CharCodes.put(pKey.toLowerCase(),AlphaNumCharCodes.get(pKey) - (65) + 26);
            }
            //numerics
            for (Integer i=0; i<=9; i++){
                AlphaNumCharCodes.put(string.valueOf(i),i+48);
                //base 64 numeric starts from 52
                Base64CharCodes.put(string.valueOf(i), i + 52);
            }
        }
        public Boolean fitBit(String pValidFor,Integer n){
            //the list of bytes
            List<Integer> pBytes = new List<Integer>();
            //multiply by 6 since base 64 uses 6 bits
            Integer bytesBeingUsed = (pValidFor.length() * 6)/8;
            //will be used to hold the full decimal value
            Integer pFullValue = 0;
            //must be more than 1 byte
            if (bytesBeingUsed <= 1)
                return false;
            //calculate the target bit for comparison
            Integer bit = 7 - (Math.mod(n,8));
            //calculate the octet that has in the target bit
            Integer targetOctet = (bytesBeingUsed - 1) - (n >> bytesBeingUsed);
            //the number of bits to shift by until we find the bit to compare for true or false
            Integer shiftBits = (targetOctet * 8) + bit;
            //get the base64bytes
            for(Integer i=0;i<pValidFor.length();i++){
                //get current character value
                pBytes.Add((Base64CharCodes.get((pValidFor.Substring(i, i+1)))));
            }
            //calculate the full decimal value
            for (Integer i = 0; i < pBytes.size(); i++){
                Integer pShiftAmount = (pBytes.size()-(i+1))*6;//used to shift by a factor 6 bits to get the value
                pFullValue = pFullValue + (pBytes[i] << (pShiftAmount));
            }
            //shift to the bit which will dictate true or false
            Integer tBitVal = ((Integer)(Math.Pow(2, shiftBits)) & pFullValue) >> shiftBits;
            return  tBitVal == 1;
        }
     }  
 }

Let m eknow any issue .

Thanks 
Manoj
Vablet DeveloperVablet Developer
Same issue as the code I linked to. Won't work with all dependent picklists. Does work with some.
ManojjenaManojjena
Hi Vablet ,

Above code is working for me . Can you  please tell me the issue what exactly you are facing ?

Thanks
Manoj
 
Vablet DeveloperVablet Developer
In the salesforce one application, the option shows that there are picklists for a selected option.

When either code is used, the map returned says that there are NO items for that selected option. This is incorrect according to the Salesforce one application.

Now many of the items work, but not ALL of them and that is the issue.
Nicolás FrancoNicolás Franco
Manojj, great solution!

You've saved my day.

Thanks!
Suvankar Chakraborty 21Suvankar Chakraborty 21
For all picklist values the above code is not working..What is the solution for this kind of error?
ManojjenaManojjena
HI Suvankar,
Can you post the pick list values for which code is not working ,I think we have to modify littele bit in code to make it functional .

Please let us know your problem .
Thanks 
Mnaoj
Suvankar Chakraborty 21Suvankar Chakraborty 21
When I added new value in controllt field.
ing field ,It is not coming into dependen
Suvankar Chakraborty 21Suvankar Chakraborty 21
*controlling
ManojjenaManojjena
Hi Suvankar,

Still I am not able to understand your problem .Please explain your problem clearly .So that I will help you .
Suvankar Chakraborty 21Suvankar Chakraborty 21
When new value is added in controlling field or dependent field it is not showing in vf page.
ManojjenaManojjena
HI Suvankar ,

If have atleast one dependent pick list for each controlling field then it will work for sure .
Rahul GawadeRahul Gawade
@Manojj:
For smaller picklists where i have 5-6 picklist values it is working fine...But when i have 240 values in controlling field and around 340 values in dependent picklist it doesn't works properly. In the map i am getting the keys values properly i.e have 240 value... but i m getting repeated dependent values in many of the controlling field. Like i am getting value1 in key1 and key2 and many other values getting repeated in the other keys. And can you share anything related to test classes for it.
Note: Not all my controlling fields have dependent pikclist values.
Dr. P. ColemanDr. P. Coleman
I don't know if this will work for picklists that large, but our use case was simple: Given a specific parent value (as a function parm), get all the dependent values mapped to it, returned as an array of SelectOption elements.  Below is our code. You will have to test it on your lists to see if you need to move the serialization method outside of the loop.
 
global static List<SelectOption> getDependentSelectOptions(String objectType, String controllerName, String dependentFieldName, String parentValue) {
   List<SelectOption> dependentItems = new list<SelectOption>();
   if(null!=objectType && null!=controllerName && null!=dependentFieldName && null!=parentValue){
       Schema.DescribeFieldResult dependentField;
       Integer parentValueIndex = -1;
       
       //FIRST get the Parent PL's index value
       Schema.DescribeSObjectResult objectMeta = Schema.describeSObjects(new String[]{objectType})[0];
       Schema.SObjectField[] fields = objectMeta.fields.getMap().values();
       for (Schema.SObjectField f: fields) {
           Schema.DescribeFieldResult d = f.getDescribe();
           String fieldname = d.getName().toLowerCase();
           String ftype = String.valueOf(d.getType()).toLowerCase();
           if (fieldname.equals(controllerName.toLowerCase()) && ('picklist'.equals(ftype) || 'multipicklist'.equals(ftype))) {
               Schema.PicklistEntry[] pplvalues = d.getPicklistValues();
               for(Integer i=0; i<pplvalues.size(); i++) {
                   if(parentValue.equals(pplvalues[i].getValue())){
                       parentValueIndex = i;
                       break;
                   }
               }
           }
           if(fieldname.equals(dependentFieldName.toLowerCase()) && ('picklist'.equals(ftype) || 'multipicklist'.equals(ftype))) {
                dependentField = d;
           }
       }

       //2nd get the dependent PL values mapped to the target parent PL's value
       if(-1!=parentValueIndex && null!=dependentField ){
           Schema.PicklistEntry[] plValues = dependentField.getPicklistValues();
           for (PicklistEntry plv: plValues) {
               String jsonstr = JSON.serialize(plv);
               Map<String,String> jMap = (Map<String,String>) JSON.deserialize(jsonstr, Map<String,String>.class);
               String validFor = jMap.get('validFor');
               String plvalue = jMap.get('value');
               if(null!=validFor && !''.equals(validFor.trim()) && isDependentValue(parentValueIndex,validFor)){
                   dependentItems.add(new SelectOption(plvalue, plvalue));
               }
           }
       }
   }
   return dependentItems;
}

global static Boolean isDependentValue(Integer index, String validFor) { 
   String decoded = EncodingUtil.convertToHex(EncodingUtil.base64Decode(validFor));
   Integer bits = hexToInt(decoded);
   return ( ( bits & (128>>Math.mod(index,8)) ) != 0 );
}

private static Map<String,Integer> hexMap = new Map<String, Integer>{'0'=>0,'1'=>1,'2'=>2,'3'=>3,'4'=>4,'5'=>5,'6'=>6,'7'=>7,'8'=>8,'9'=>9,'A'=>10,'B'=>11,'C'=>12,'D'=>13,'E'=>14,'F'=>15,'a'=>10,'b'=>11,'c'=>12,'d'=>13,'e'=>14,'f'=>15};
global static Integer hexToInt(String hex) {
    Integer retVal = 0;
    for(Integer i=0;i<hex.length();i+=2) {
        retVal += (hexMap.get(hex.substring(i,i+1)) * 16) + (hexMap.get(hex.substring(i+1,i+2)));
    }
    return retVal;
}

 
kumar tecnodevkumar tecnodev
Hi Manojjena

I have implemented the same code you have written .But every i am getting all value of the dependent picklist .

Like for controlling value "ABC" is Dependent on Dependent Value "ABC".
So its should Show on ABC.
Instead of this its showing all the value of Dependent picklist.
hugogmendeshugogmendes
@Manojjena Thank you very much, it works perfectly for me!
Glyn Anderson (Slalom)Glyn Anderson (Slalom)
Here's another implementation that uses a single method, about 30 lines of Apex, and returns the dependent picklist values for every controlling field value: https://glyntalkssalesforce.blogspot.com/2018/08/dependent-picklist-values-in-apex.html

The blog post also includes an explanation of the problem, a breakdown of how the code works, and test code that provides 100% coverage with assertions.

I hope someone finds it useful!
Vijay Kumar 756Vijay Kumar 756

Hi Manojjena,

 

I have implemented your code and its working perfect,

Thanks