Tuesday, 16 August 2011

Salesforce Dependent Picklists

Salesforce has forgot to allow dependent picklists to be interrogatible by Apex.

If you are wanting to display dependent picklists on a VF page using a standardcontroller it easy, just use inputFields:

<apex:page standardController="Account">
    <apex:form >
        <apex:pageBlock mode="edit">
            <apex:pageBlockButtons >
                <apex:commandButton action="{!save}" value="Save"/>
            <apex:pageBlockSection title="Dependent Picklists" columns="2">
            <apex:inputField value="{!account.industry}"/>
            <apex:inputField value="{!account.subcategories__c}"/>

However it becomes almost impossible when you use a custom controller

Options I considered was to use a javascript hack ( attached ), which although works on most pages the javascript reacts badly with the rendered visualforce elements especially when using rerenders etc
Code on VF page
    <apex:includeScript value="{!$Resource.Prototype}"/>  
    <apex:includeScript value="/soap/ajax/15.0/connection.js"/>
    <apex:includeScript value="{!$Resource.DependentPicklists}"/> 

                        <apex:outputLabel styleClass="RightBold">Status</apex:outputLabel>
                        <apex:inputField onclick="helper.handleControllerChange()" styleClass="LeftAlign"  id="controllingField" value="{!aTask.Status}"/>
                        <apex:outputLabel styleClass="RightBold">ControlledField</apex:outputLabel><br />
                        <apex:selectList id="dependentField" styleClass="LeftAlign" value="{!aTask.ControlledField__c}">
                            <apex:selectOption itemValue="None" />

                             var dependentField = '{!$Component.dependentField}'; 
                            var controllingField = '{!$Component.controllingField}';             
                        <script type="text/javascript">
                               var helper;                                 
                               document.observe("dom:loaded", function(){
                                   helper = new DependentPicklistHelper(
                                       controllingField ,dependentField

I also looked at apex interrogating the dependent picklists to produce selectlists on the VF page, but Salesforce has just forgot to provide this capability. Hence, this is where the problem begins.

So I considered adding an apex iframe which embeds another VF page and the controller to this page sets static variables in another class. The controller of the page that has the embedded iframe then uses the same static variables
which would hold the same value already set. This option could well be a valid workaround in some scenarios.

However I opted for a slick solution using static resources. I upload a csv file containing the values for the dependent picklists. I created a function to read the static resource and create a Map of the values. So now I have a Map representing the
exact values of the dependent picklists. All I needed to do now is display the values on the VF page and make the selectlist for the controlling picklist and dependent picklist to be linked.

To see this in action have a look at the following functions

//Note you need to create a csv file with matrix for the dependent picklist values and upload to Static Resources

public List<SelectOption> ControlledFieldValues{get;set;}
public Task aTask {get;set;}

public static List<SelectOption> getMySelectOptions(String thisObject, String thisField, List<String> excluded, Boolean optionalBlank) {

        List<SelectOption> options = new List<SelectOption>();
        if (optionalBlank)
            options.add(new SelectOption('' ,'' ));
            List<Schema.PicklistEntry> returnPicks = getMyPickValues(thisObject, thisField);
            system.debug('returnPicks ' + returnPicks);
            if (returnPicks == null)
                return null;
            String PickVal;
            Boolean ExclusionExists;
            for (Schema.PicklistEntry EachVal: returnPicks){
                PickVal = String.valueof(EachVal.getValue());
                //search each Exclusion for this PickVal
                for (String EachExclusion: excluded){
                    system.debug('PickVal ' + PickVal + ' ' + EachExclusion);
                    if (PickVal != EachExclusion)
                        options.add(new SelectOption(PickVal,PickVal)); //add PickVal if it is not in the Exclusion
        catch(Exception e){system.debug(' ' + e);}
        return options;   

public static List<Schema.PicklistEntry> getMyPickValues(String thisobject,String thisFieldName){  

    List<Schema.PicklistEntry> picklists;
        Map<String, Schema.SObjectType> thisPickListMp = Schema.getGlobalDescribe();
        Schema.SObjectType objType = thisPickListMp.get(thisobject) ;
        Schema.DescribeSObjectResult objResult = objType.getDescribe() ;
        Map<String, Schema.SObjectField> fields = objResult.fields.getMap() ;

        Schema.SObjectField selectedObjField = fields.get(thisFieldName) ;
        Schema.DescribeFieldResult fieldResult = selectedObjField.getDescribe() ;
        picklists = fieldResult.getMyPickValues();
    catch(Exception e){system.debug(' ' + e);return null;}
    return picklists;

    public List<SelectOption> getTheseTaskStatus() {
            //gets the picklists
            List<SelectOption> options = new List<SelectOption>();
                String[] excluded = new String[]{''};
                options = getMySelectOptions('<<object>>','<<picklist field>>',excluded,false);
            return options;

public List<StaticResource> thisStaticResource;

thisStaticResource = [Select Id, body, contentType, bodyLength, description,Name from StaticResource where Name=:fileName];

public Map<String, list<String>> getMapProperties() {
                                    Map<String, list<String>> propertiesMap;
                                    if(thisStaticResource == null || thisStaticResource.size() == 0) {
                                    for(StaticResource sr : thisStaticResource) {
                                                Blob propBlob = sr.body;
                                                String bdy = propBlob.toString();
                                                List<String> propertyList = propBlob.toString().split('\r',0);

                                                propertiesMap = new Map<String, list<String>>();
                                                String KeyVal;
                                                for(String property : propertyList) {                                                 
                                                                        list<String> newLst = new list<String>();
                                                                        List<String> PicklistValues = property.split(',');

                                                                        KeyVal = PicklistValues[0];
                                                                        if(PicklistValues != null && PicklistValues.size() > 0)
                                    //if it reaches here the value was not found
                                    return propertiesMap;

    public Pagereference getControlFieldVals() {
            List<SelectOption> options = new List<SelectOption>();              
            list<String> eachRow;
            if (mpResourceList != null){
                        if (aTask == null){
                                    eachRow = mpResourceList.get(<<status>>);
                                    for (String eachItem: eachRow){
                                                options.add(new SelectOption(eachItem,eachItem));
                                    eachRow = mpResourceList.get(aTask.Status);                                       
                                    for (String eachItem: eachRow){
                                                options.add(new SelectOption(eachItem,eachItem));
            ControlledFieldValues = options;
            return null;

<apex:outputPanel id="refreshControlledFieldResult">
            <apex:selectList size="1" required="false" value="{!aTask.Status}">
                        <apex:selectOptions value="{!TaskStatus}" />
                        <apex:actionsupport event="onchange" action="{!getControlFieldVals}" rerender="refreshControlledFieldResult"/>

            <apex:selectList size="1"  id="dependentField" required="false" value="{!aTask.2ndfield}">
                        <apex:selectOptions value="{!ControlledFieldValues}" />

I'm sure with a bit of intrepid versatility you will be able decipher the rest

No comments:

Post a Comment

Note: only a member of this blog may post a comment.