Find your content:

Search form

You are here

Get lists of dependent picklist options in Apex

 
Share

I have a pair of dependent picklists. In Apex code, how can I determine what options are valid in the dependent field for each option in the controlling field?

I've tried using getPicklistValues(), but there doesn't seem to be any way of getting dependency information.

Example

Controlling_Field__c

Fruit, Vegetable, Dairy

Dependent_Field__c

(Fruit): Apple, Banana, Pear

(Vegetable): Tomato, Egglant, Lettuce

(Dairy): Milk, Cheese, Yogurt

What I'm Looking For

(In pseudocode, something like:)

controllingOptions = dependentfield.getPicklistValues();
for (option : controllingOptions) {
    dependentOptions = dependentfield.getPicklistValuesFor(option);
    do_something_with_it();
}

Attribution to: Benj

Possible Suggestion/Solution #1

Salesforce doesn't reveal information about dependent picklists in normal Describe calls in Apex, but it is included in calls via the API, and can also be accessed through Apex by serializing and deserializing PicklistEntry objects (see gist linked below for an example of how to do that).

Each PicklistEntry in a dependent field has a property called validFor which contains a Base64 encoded string. When decoded to bits, each bit read left to right corresponds to an option in the controlling field. For example, if you have a validFor of "gAAA"

example pke.validFor: g      A      A      A
displayed as bits:    100000 000000 000000 000000
rearranged as bytes:  10000000 00000000 00000000

As such, you can use loop through all of the picklist values and use bitwise operators to check whether each dependent value is valid for each controlling value.

I wrote a javascript function and Apex Class to abstract all this, which is available as a gist. You can use it like this (assumes you've correctedly loaded the Ajax toolkit in a visualforce page):

/* Build an Object in which keys are valid options for the controlling field
 * and values are lists of valid options for the dependent field.
 */
var OBJ_NAME = 'Custom_Object__c';
var CTRL_FIELD_NAME = "Controlling_Field__c";
var DEP_FIELD_NAME = "Dependent_Field__c";
var options = getDependentOptions(OBJ_NAME, CTRL_FIELD_NAME, DEP_FIELD_NAME);
console.debug(options);

Attribution to: Benj

Possible Suggestion/Solution #2

So, this awesome dude, wrote exactly what you were looking for. Might as well pimp his code and spread the word. This was a life save for something I was working on this week!

http://titancronus.com/blog/2014/05/01/salesforce-acquiring-dependent-picklists-in-apex/


Attribution to: Tommy

Possible Suggestion/Solution #3

I've recently come up with this concise version of the dependent picklist code. One method, self-contained, about 30 lines of code. The method returns a map of lists, keyed by controlling values, the lists contain all valid dependent values for the controlling value. This works when the controlling field is a checkbox (boolean) as well as a picklist.

public static Map<Object,List<String>> getDependentPicklistValues( Schema.sObjectField dependToken )
{
    Schema.DescribeFieldResult depend = dependToken.getDescribe();
    Schema.sObjectField controlToken = depend.getController();
    if ( controlToken == null ) return null;
    Schema.DescribeFieldResult control = controlToken.getDescribe();
    List<Schema.PicklistEntry> controlEntries =
    (   control.getType() == Schema.DisplayType.Boolean
    ?   null
    :   control.getPicklistValues()
    );

    String base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    Map<Object,List<String>> dependentPicklistValues = new Map<Object,List<String>>();
    for ( Schema.PicklistEntry entry : depend.getPicklistValues() ) if ( entry.isActive() )
    {
        List<String> base64chars =
            String.valueOf
            (   ((Map<String,Object>) JSON.deserializeUntyped( JSON.serialize( entry ) )).get( 'validFor' )
            ).split( '' );
        for ( Integer index = 0; index < (controlEntries != null ? controlEntries.size() : 2); index++ )
        {
            Object controlValue =
            (   controlEntries == null
            ?   (Object) (index == 1)
            :   (Object) (controlEntries[ index ].isActive() ? controlEntries[ index ].getLabel() : null)
            );
            Integer bitIndex = index / 6, bitShift = 5 - Math.mod( index, 6 );
            if  (   controlValue == null
                || (base64map.indexOf( base64chars[ bitIndex ] ) & (1 << bitShift)) == 0
                ) continue;
            if ( !dependentPicklistValues.containsKey( controlValue ) )
            {
                dependentPicklistValues.put( controlValue, new List<String>() );
            }
            dependentPicklistValues.get( controlValue ).add( entry.getLabel() );
        }
    }
    return dependentPicklistValues;
}

I've put this all into a blog post here: Glyn Talks Salesforce. The blog post includes details of how it works and test code for 100% coverage with assertions.

I hope it helps!


Attribution to: Glyn Anderson

Possible Suggestion/Solution #4

I have had an opportunity to check the solution from Tommy answer (http://titancronus.com/blog/2014/05/01/salesforce-acquiring-dependent-picklists-in-apex/), and while it worked for some simple picklist dependency picklist entries, it did not work for some specific dependent picklist options. Specifically, if the dependent list of picklist options is large, or if the controlling picklist has many options - some values were marked as dependent, and others were not...

I have managed to implement the Bitset helper class, which is based on the approach provided in the Salesforce documentation (see PicklistEntry, Java code sample), see below:

public class BitsetChecker {
    public String validFor {get;private set;}
    public String vfDecoded {get;private set;}
    public String[] hexBytes {get;private set;}
    public Integer[] bytes {get;private set;}

    public BitsetChecker(String validFor) {
        this.validFor = validFor;
        this.vfDecoded = null;
        hexBytes = new String[] {};
        bytes = new Integer[] {};
        if (String.isNotBlank(validFor)) {
            this.vfDecoded = String.isNotBlank(validFor) ?
                EncodingUtil.convertToHex(EncodingUtil.base64Decode(validFor)).toLowerCase() : '';
            if (String.isNotBlank(vfDecoded) && Math.mod(vfDecoded.length(), 2) == 0) {
                for (Integer i = 0; i < vfDecoded.length(); i += 2) {
                    String hexByte = vfDecoded.substring(i, i + 2);
                    hexBytes.add(hexByte);
                    bytes.add(hexToDecimal(hexByte).intValue());
                }
            }
        }
    }

    public Boolean testBit(Integer n) {
        Boolean result = false;
        if (n != null && n < size() && hexBytes != null) {
            Integer bytesPos = n >> 3;
            Integer targetByte = bytesPos < bytes.size() ? bytes[bytesPos] : null;
            if (targetByte != null) {
                Integer mask = 128 >> Math.mod(n, 8);
                Integer maskedByte = targetByte & mask;
                result = maskedByte != 0;
            }
        }
        return result;
    }

    public Integer size() {
        return bytes.size() * 8;
    }

    public static Decimal hexToDecimal(String sourceHex) {
        String hex = '0123456789abcdef';
        String[] hexValue = sourceHex.split('');
        Decimal result = 0;
        for(Integer index = 0; index < hexValue.size(); index++) {
            result = (result * 16) + hex.indexOf(hexValue[index]);
        }
        return result;
    }
}

Attribution to: Andrew Stopchenko

Possible Suggestion/Solution #5

Approaches had been well discussed in the above thread. Following is an apex utility function that provides the controlling field value and its dependent field values provided object name, controlling field and dependent field.

//By SharSolutions
   public class MyPickListInfo
   {
        public String validFor;
   }

public static Map<String, List<String>> getFieldDependencies(String objectName, String controllingField, String dependentField)
{
    Map<String, List<String>> controllingInfo = new Map<String, List<String>>();

    Schema.SObjectType objType = Schema.getGlobalDescribe().get(objectName);

    Schema.DescribeSObjectResult describeResult = objType.getDescribe();
    Schema.DescribeFieldResult controllingFieldInfo = describeResult.fields.getMap().get(controllingField).getDescribe();
    Schema.DescribeFieldResult dependentFieldInfo = describeResult.fields.getMap().get(dependentField).getDescribe();

    List<Schema.PicklistEntry> controllingValues = controllingFieldInfo.getPicklistValues();
    List<Schema.PicklistEntry> dependentValues = dependentFieldInfo.getPicklistValues();

    for(Schema.PicklistEntry currControllingValue : controllingValues)
    {
        System.debug('ControllingField: Label:' + currControllingValue.getLabel());
        controllingInfo.put(currControllingValue.getLabel(), new List<String>());
    }

    for(Schema.PicklistEntry currDependentValue : dependentValues)
    {
        String jsonString = JSON.serialize(currDependentValue);

        MyPickListInfo info = (MyPickListInfo) JSON.deserialize(jsonString, MyPickListInfo.class);

        String hexString = EncodingUtil.convertToHex(EncodingUtil.base64Decode(info.validFor)).toUpperCase();

        System.debug('DependentField: Label:' + currDependentValue.getLabel() + ' ValidForInHex:' + hexString + ' JsonString:' + jsonString);

        Integer baseCount = 0;

        for(Integer curr : hexString.getChars())
        {
            Integer val = 0;

            if(curr >= 65)
            {
                val = curr - 65 + 10;
            }
            else
            {
                val = curr - 48;
            }

            if((val & 8) == 8)
            {
                System.debug('Dependent Field: ' + currDependentValue.getLabel() + ' Partof ControllingField:' + controllingValues[baseCount + 0].getLabel());
                controllingInfo.get(controllingValues[baseCount + 0].getLabel()).add(currDependentValue.getLabel());
            }
            if((val & 4) == 4)
            {
                System.debug('Dependent Field: ' + currDependentValue.getLabel() + ' Partof ControllingField:' + controllingValues[baseCount + 1].getLabel());
                controllingInfo.get(controllingValues[baseCount + 1].getLabel()).add(currDependentValue.getLabel());                    
            }
            if((val & 2) == 2)
            {
                System.debug('Dependent Field: ' + currDependentValue.getLabel() + ' Partof ControllingField:' + controllingValues[baseCount + 2].getLabel());
                controllingInfo.get(controllingValues[baseCount + 2].getLabel()).add(currDependentValue.getLabel());                    
            }
            if((val & 1) == 1)
            {
                System.debug('Dependent Field: ' + currDependentValue.getLabel() + ' Partof ControllingField:' + controllingValues[baseCount + 3].getLabel());
                controllingInfo.get(controllingValues[baseCount + 3].getLabel()).add(currDependentValue.getLabel());                    
            }

            baseCount += 4;
        }            
    } 

    System.debug('ControllingInfo: ' + controllingInfo);

    return controllingInfo;
}

Attribution to: Suriya Soundrapandian

Possible Suggestion/Solution #6

I had the same problem a while ago and I have posted on ideaexchange:

https://sites.secure.force.com/success/ideaView?id=08730000000h1y6AAA

At this point in time this is the only solution that I have found:

http://iwritecrappycode.wordpress.com/2012/02/23/dependent-picklists-in-salesforce-without-metadata-api-or-visualforce/

Hope that helps!


Attribution to: Boris Bachovski

Possible Suggestion/Solution #7

you can take help from this url https://www.minddigital.com/how-to-create-dynamic-dependent-picklist-of-objects-within-salesforce/ where apex class is there to ceate the logic and just need to replace the custom object with the object on which you want to work for dependent picklist and to implement that logic we have a VF page so I guess that will help you alot.


Attribution to: Ajay Dubedi

Possible Suggestion/Solution #8

Thank for your solution i was using code from https://developer.salesforce.com/forums/?id=906F0000000BIVqIAO which worked fine but not for the dependent values of the last value of the controller Picklist. In this case, the validfor.length of the dependent values is 8.

With your solution it works. Here is an exemple of Apex code calling your Class "BitsetChecker"

for(Integer ppli=0; ppli<pplvalues.size() ; ppli++) {
   list <string> DependentItemList = new list <string>();
   for (PicklistEntry dplv: dplvalues) {
       String jsonstr = JSON.serialize(dplv);
       Map<String,String> jMap = (Map<String,String>)     
       JSON.deserialize(jsonstr, Map<String,String>.class);
        String validFor = jMap.get('validFor');
       String dplvalue = jMap.get('value');
       // using class Bitsetchecker
       BitsetChecker checkdependency=new BitsetChecker(validfor);
        if (checkdependency.testbit(ppli)) {
           DependentItemList.add(dplv.getValue());                     
        } // dependent found
     } // for dependent picklist
} // for parent picklist

Attribution to: jf foglierini
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/4462

My Block Status

My Block Content