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