Dependent Picklist Using Lightning Component

The number of base lightning component is increasing in each release. But there are still few missing area. One of them is Dependent picklist. So today we will create Dependent picklist using Lightning:select.

This is a dynamic component, we just need to pass object name and dependent and controlling field. It will give you selected values. So if in your org Country and State picklist is enable. You can use this component to create same for custom object and store the value.

Lightning Dependent Picklist

First we will create the component

LightningDependentPicklistCmp.cmp

<aura:component controller="LightningDependentPicklistCmpController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
	  <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
   
    <!-- aura attributes--> 
    <aura:attribute name="parentList" type="list" default="[]" description=""/>
    <aura:attribute name="childList" type="list" default="[]"/>
    <aura:attribute name="pickListMap" type="map"/>
    <aura:attribute name="disabledChildField" type="boolean" default="true"/>
    
    <aura:attribute name="objectName" type="string" default="Account"/>
    <aura:attribute name="parentFieldAPI" type="string" default="Active__c"/>
    <aura:attribute name="childFieldAPI" type="string" default="CustomerPriority__c"/>
    <aura:attribute name="parentFieldLabel" type="string" />
    <aura:attribute name="childFieldLabel" type="string"/>
    <aura:attribute name="parentValue" type="string" default=""/>
    <aura:attribute name="childValue" type="string" default=""/>
    
    
   <lightning:card title="Dependent Picklist Demo">
        <!-- Controller Field -->
        <lightning:layoutItem size="12" padding="around-small">    
            <lightning:select name="parentField" aura:id="parentField"
                              value="{!v.parentValue}"
                              label="{!v.parentFieldLabel}"
                              onchange="{!c.parentFieldChange}">
                <aura:iteration items="{!v.parentList}" var="value">
                    <option value="{!value}">{!value}</option>
                </aura:iteration>
            </lightning:select>
        </lightning:layoutItem>
        
        <!--Dependent Field-->
        <lightning:layoutItem size="12" padding="around-small">
            <lightning:select name="childField"
                              value="{!v.childValue}"
                              label="{!v.childFieldLabel}"
                              disabled="{!v.disabledChildField}">
                <aura:iteration items="{!v.childList}" var="value">
                    <option value="{!value}">{!value}</option>
                </aura:iteration>
            </lightning:select>
        </lightning:layoutItem>
    </lightning:card> 
</aura:component>

LightningDependentPicklistCmpController.Js

({
	doInit : function(component, event, helper) {
		var action = component.get("c.getDependentPicklist");
        action.setParams({
            ObjectName : component.get("v.objectName"),
            parentField : component.get("v.parentFieldAPI"),
            childField : component.get("v.childFieldAPI")
        });
        
        action.setCallback(this, function(response){
         	var status = response.getState();
            if(status === "SUCCESS"){
                var pickListResponse = response.getReturnValue();
                
                //save response 
                component.set("v.pickListMap",pickListResponse.pickListMap);
                component.set("v.parentFieldLabel",pickListResponse.parentFieldLabel);
                component.set("v.childFieldLabel",pickListResponse.childFieldLabel);
                
                // create a empty array for store parent picklist values 
                var parentkeys = []; // for store all map keys 
                var parentField = []; // for store parent picklist value to set on lightning:select. 
                
                // Iterate over map and store the key
                for (var pickKey in pickListResponse.pickListMap) {
                    parentkeys.push(pickKey);
                }
                
                //set the parent field value for lightning:select
                if (parentkeys != undefined && parentkeys.length > 0) {
                    parentField.push('--- None ---');
                }
                
                for (var i = 0; i < parentkeys.length; i++) {
                    parentField.push(parentkeys[i]);
                }  
                // set the parent picklist
                component.set("v.parentList", parentField);
                
            }
        });
        
        $A.enqueueAction(action);
	},
    
    parentFieldChange : function(component, event, helper) {
    	var controllerValue = component.find("parentField").get("v.value");// We can also use event.getSource().get("v.value")
        var pickListMap = component.get("v.pickListMap");

        if (controllerValue != '--- None ---') {
             //get child picklist value
            var childValues = pickListMap[controllerValue];
            var childValueList = [];
            childValueList.push('--- None ---');
            for (var i = 0; i < childValues.length; i++) {
                childValueList.push(childValues[i]);
            }
            // set the child list
            component.set("v.childList", childValueList);
            
            if(childValues.length > 0){
                component.set("v.disabledChildField" , false);  
            }else{
                component.set("v.disabledChildField" , true); 
            }
            
        } else {
            component.set("v.childList", ['--- None ---']);
            component.set("v.disabledChildField" , true);
        }
	}
    
})

LightningDependentPicklistCmpController.cls

public with sharing class LightningDependentPicklistCmpController {
	private static final String base64Chars = '' +
        'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
        'abcdefghijklmnopqrstuvwxyz' +
        '0123456789+/';
    
    @AuraEnabled 
    public static PicklistWrapper getDependentPicklist(String ObjectName, string parentField, string childField) {
        Map<String,List<String>> pickListMap = new Map<String,List<String>>();
        PicklistWrapper pw = new PicklistWrapper();
        pw.pickListMap = pickListMap;
        
        if (Schema.getGlobalDescribe().get(ObjectName) ==null || String.isBlank(parentField) || String.isBlank(ChildField)){
            return pw;
        }
 
        Schema.sObjectType objType = Schema.getGlobalDescribe().get(ObjectName).newSObject().getSObjectType();
        Map<String, Schema.SObjectField> objFieldMap = objType.getDescribe().fields.getMap();
        
        if (!objFieldMap.containsKey(parentField) || !objFieldMap.containsKey(childField)){
            return pw;     
        }
        
        List<PicklistEntryWrapper> depEntries = (List<PicklistEntryWrapper>)JSON.deserialize(JSON.serialize(objFieldMap.get(ChildField).getDescribe().getPicklistValues()), List<PicklistEntryWrapper>.class);
        List<String> controllingValues = new List<String>();
        
        for (Schema.PicklistEntry ple : objFieldMap.get(parentField).getDescribe().getPicklistValues()) {
            pickListMap.put(ple.getLabel(), new List<String>());
            controllingValues.add(ple.getLabel());
        }
        
        for (PicklistEntryWrapper plew : depEntries) {
            String validForBits = base64ToBits(plew.validFor);
            for (Integer i = 0; i < validForBits.length(); i++) {
                String bit = validForBits.mid(i, 1);
                if (bit == '1') {
                    pickListMap.get(controllingValues.get(i)).add(plew.label);
                }
            }
        }
        
        pw.pickListMap = pickListMap;
        pw.parentFieldLabel = objFieldMap.get(parentField).getDescribe().getLabel();
        pw.childFieldLabel = objFieldMap.get(childField).getDescribe().getLabel();
        return pw;
    }
    
    //Refer from here https://salesforce.stackexchange.com/questions/4462/get-lists-of-dependent-picklist-options-in-apex
    public static String decimalToBinary(Integer val) {
        String bits = '';
        while (val > 0) {
            Integer remainder = Math.mod(val, 2);
            val = Integer.valueOf(Math.floor(val / 2));
            bits = String.valueOf(remainder) + bits;
        }
        return bits;
    }
    
    public static String base64ToBits(String validFor) {
        if (String.isEmpty(validFor)) return '';
        
        String validForBits = '';
        
        for (Integer i = 0; i < validFor.length(); i++) {
            String thisChar = validFor.mid(i, 1);
            Integer val = base64Chars.indexOf(thisChar);
            String bits = decimalToBinary(val).leftPad(6, '0');
            validForBits += bits;
        }
        
        return validForBits;
    }
    
    public class PicklistWrapper{
    	@AuraEnabled
        public Map<String, List<String>> pickListMap;
        @AuraEnabled
        public String parentFieldLabel;
        @AuraEnabled
        public String childFieldLabel;      
    }
        
    public class PicklistEntryWrapper{
        public String active;
        public String defaultValue;
        public String label;
        public String value;
        public String validFor;
        
    }
}

As this a dynamic component so it handle field label as well. You can use this component in your existing app easily.

Did you like this post or want to add anything let me know in comments section. Happy programming 🙂

Advertisements

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.