+ Start a Discussion
Manish Kulkarni 12Manish Kulkarni 12 

How to display multilevel dependant picklist in lightning component? For example: Country must control values in State and inturn State must control the values to be displayed in city field. Any help on this will be appreciated!

Best Answer chosen by Manish Kulkarni 12
sfdcMonkey.comsfdcMonkey.com
Here is the sample code of dependent picklist for 2 levels (2 fields) in salesforce lightning component:
http://sfdcmonkey.com/2018/08/31/dependent-picklist-lightningselect-lightning-salesforce/

if you want to create 3 level fields dependency then you need to make some changes in above post : 

here is the example with 3 fields dependency.. 

---- Country
    ---- City [controller field Country]
      ----- language [controller field City ]
 
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction"
                access="global"
                controller="dependentPicklist_UpdateCtrl">
    <!-- call doInit function on component load -->  
    <aura:handler name="init" value="this" action="{!c.doInit}"/>
    
    <!-- aura attributes-->  
    <aura:attribute name="listControllingValues" type="list" default="[]" description="to store controller field values"/>
    <aura:attribute name="listDependingValues" type="list" default="['--- None ---']" description="to store dependent field values"/>
    <aura:attribute name="listSubDependingValues" type="list" default="['--- None ---']" description="to store dependent field values"/>
    
    <aura:attribute name="depnedentFieldMap" type="map" description="map to store dependent values with controlling value"/>
    <aura:attribute name="subDepnedentFieldMap" type="map" description="map to store sub dependent values with controlling value"/>
    <aura:attribute name="bDisabledDependentFld" type="boolean" default="true"/> 
    <aura:attribute name="bDisabledSubDependentFld" type="boolean" default="true"/> 
    
    
    <aura:attribute name="objDetail" type="contact" default="{'sobjectType' : 'contact'}"/>
    <aura:attribute name="controllingFieldAPI" type="string" default="Country__c" description="store field API name of Controller field"/>
    <aura:attribute name="dependingFieldAPI" type="string" default="City__c" description="store field API name of dependent field"/>
    <aura:attribute name="subDependingFieldAPI" type="string" default="Language__c" description="store field API name of sub dependent field"/>
    
    <!--Controller Field-->
    <lightning:layoutItem size="12" padding="around-small">    
        <lightning:select name="controllerFld"
                          value="{!v.objDetail.Country__c}"
                          label="Country"
                          onchange="{!c.onControllerFieldChange}">
            <aura:iteration items="{!v.listControllingValues}" var="val">
                <option value="{!val}">{!val}</option>
            </aura:iteration>
        </lightning:select>
    </lightning:layoutItem>
    
    <!--Dependent Field-->
    <lightning:layoutItem size="12" padding="around-small">
        <lightning:select name="dependentFld"
                          value="{!v.objDetail.City__c}"
                          label="City"
                          disabled="{!v.bDisabledDependentFld}"
                          onchange="{!c.onSubControllerFieldChange}">
            <aura:iteration items="{!v.listDependingValues}" var="val">
                <option value="{!val}">{!val}</option>
            </aura:iteration>
        </lightning:select>
    </lightning:layoutItem>
    
    <!--sub Dependent Field-->
    <lightning:layoutItem size="12" padding="around-small">
        <lightning:select name="subDependentFld"
                          value="{!v.objDetail.Language__c}"
                          label="language"
                          disabled="{!v.bDisabledSubDependentFld}">
            <aura:iteration items="{!v.listSubDependingValues}" var="val">
                <option value="{!val}">{!val}</option>
            </aura:iteration>
        </lightning:select>
    </lightning:layoutItem>
    
</aura:component>

js controller
({
    doInit : function(component, event, helper) { 
        // get the fields API name and pass it to helper function  
        var controllingFieldAPI = component.get("v.controllingFieldAPI");
        var dependingFieldAPI = component.get("v.dependingFieldAPI");
        var subDependingFieldAPI = component.get("v.subDependingFieldAPI");
        
        var objDetails = component.get("v.objDetail");
        // call the helper function
        helper.fetchPicklistValues(component,objDetails,controllingFieldAPI, dependingFieldAPI, "v.depnedentFieldMap");
        
        // 2nd and 3ed picklist 
        helper.fetchPicklistValues(component,objDetails,dependingFieldAPI, subDependingFieldAPI, "v.subDepnedentFieldMap");
    },
    
    onControllerFieldChange: function(component, event, helper) {     
        var controllerValueKey = event.getSource().get("v.value"); // get selected controller field value
        var depnedentFieldMap = component.get("v.depnedentFieldMap");
        
        if (controllerValueKey != '--- None ---') {
            // disable and reset sub dependent field 
           
           
            var ListOfDependentFields = depnedentFieldMap[controllerValueKey];
            
            if(ListOfDependentFields.length > 0){
                component.set("v.bDisabledDependentFld" , false);  
                helper.fetchDepValues(component, ListOfDependentFields,"v.listDependingValues");    
            }else{
                component.set("v.bDisabledDependentFld" , true); 
                component.set("v.listDependingValues", ['--- None ---']);
            }  
            
        } else {
            component.set("v.listDependingValues", ['--- None ---']);
            component.set("v.bDisabledDependentFld" , true);
        }
        
        component.set("v.bDisabledSubDependentFld" , true);
        component.set("v.listSubDependingValues", ['--- None ---']);
    },
    
    
    onSubControllerFieldChange : function(component, event, helper) {     
        var controllerValueKey = event.getSource().get("v.value"); // get selected sub controller field value
        var depnedentFieldMap = component.get("v.subDepnedentFieldMap");
       
        if (controllerValueKey != '--- None ---') {
            var ListOfDependentFields = depnedentFieldMap[controllerValueKey];
            if(ListOfDependentFields.length > 0){
                component.set("v.bDisabledSubDependentFld" , false);  
                helper.fetchDepValues(component, ListOfDependentFields,"v.listSubDependingValues");    
            }else{
                component.set("v.bDisabledSubDependentFld" , true); 
                component.set("v.listSubDependingValues", ['--- None ---']);
            }  
            
        } else {
            component.set("v.listSubDependingValues", ['--- None ---']);
            component.set("v.bDisabledSubDependentFld" , true);
        }
    },
})

js helper :
({
    fetchPicklistValues: function(component,objDetails,controllerField, dependentField,mapAttrName) {
        // call the server side function  
        var action = component.get("c.getDependentMap");
        // pass paramerters [object definition , contrller field name ,dependent field name] -
        // to server side function 
        action.setParams({
            'objDetail' : objDetails,
            'contrfieldApiName': controllerField,
            'depfieldApiName': dependentField 
        });
        //set callback   
        action.setCallback(this, function(response) {
            if (response.getState() == "SUCCESS") {
                //store the return response from server (map<string,List<string>>)  
                var StoreResponse = response.getReturnValue();
                // once set #StoreResponse to depnedentFieldMap attribute 
                component.set(mapAttrName,StoreResponse);
                
                if(mapAttrName == 'v.depnedentFieldMap'){

                    // create a empty array for store map keys(@@--->which is controller picklist values) 
                    var listOfkeys = []; // for store all map keys (controller picklist values)
                    var ControllerField = []; // for store controller picklist value to set on lightning:select. 
                    
                    // play a for loop on Return map 
                    // and fill the all map key on listOfkeys variable.
                    for (var singlekey in StoreResponse) {
                        listOfkeys.push(singlekey);
                    }
                    
                    //set the controller field value for lightning:select
                    if (listOfkeys != undefined && listOfkeys.length > 0) {
                        ControllerField.push('--- None ---');
                    }
                    
                    for (var i = 0; i < listOfkeys.length; i++) {
                        ControllerField.push(listOfkeys[i]);
                    }  
                    // set the ControllerField variable values to country(controller picklist field)
                    component.set("v.listControllingValues", ControllerField);
                }
            }else{
                alert('Something went wrong..');
            }
        });
        $A.enqueueAction(action);
    },
    
    fetchDepValues: function(component, ListOfDependentFields,lstAttrName) {
        // create a empty array var for store dependent picklist values for controller field  
        var dependentFields = [];
        dependentFields.push('--- None ---');
        for (var i = 0; i < ListOfDependentFields.length; i++) {
            dependentFields.push(ListOfDependentFields[i]);
        }
        // set the dependentFields variable values to store(dependent picklist field) on lightning:select
        component.set(lstAttrName, dependentFields);
        
    },
    
})

No need to change in Apex ​Controller

User-added image

Thanks , let us know if it helps you 
http://sfdcMonkey.com

All Answers

sfdcMonkey.comsfdcMonkey.com
Here is the sample code of dependent picklist for 2 levels (2 fields) in salesforce lightning component:
http://sfdcmonkey.com/2018/08/31/dependent-picklist-lightningselect-lightning-salesforce/

if you want to create 3 level fields dependency then you need to make some changes in above post : 

here is the example with 3 fields dependency.. 

---- Country
    ---- City [controller field Country]
      ----- language [controller field City ]
 
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction"
                access="global"
                controller="dependentPicklist_UpdateCtrl">
    <!-- call doInit function on component load -->  
    <aura:handler name="init" value="this" action="{!c.doInit}"/>
    
    <!-- aura attributes-->  
    <aura:attribute name="listControllingValues" type="list" default="[]" description="to store controller field values"/>
    <aura:attribute name="listDependingValues" type="list" default="['--- None ---']" description="to store dependent field values"/>
    <aura:attribute name="listSubDependingValues" type="list" default="['--- None ---']" description="to store dependent field values"/>
    
    <aura:attribute name="depnedentFieldMap" type="map" description="map to store dependent values with controlling value"/>
    <aura:attribute name="subDepnedentFieldMap" type="map" description="map to store sub dependent values with controlling value"/>
    <aura:attribute name="bDisabledDependentFld" type="boolean" default="true"/> 
    <aura:attribute name="bDisabledSubDependentFld" type="boolean" default="true"/> 
    
    
    <aura:attribute name="objDetail" type="contact" default="{'sobjectType' : 'contact'}"/>
    <aura:attribute name="controllingFieldAPI" type="string" default="Country__c" description="store field API name of Controller field"/>
    <aura:attribute name="dependingFieldAPI" type="string" default="City__c" description="store field API name of dependent field"/>
    <aura:attribute name="subDependingFieldAPI" type="string" default="Language__c" description="store field API name of sub dependent field"/>
    
    <!--Controller Field-->
    <lightning:layoutItem size="12" padding="around-small">    
        <lightning:select name="controllerFld"
                          value="{!v.objDetail.Country__c}"
                          label="Country"
                          onchange="{!c.onControllerFieldChange}">
            <aura:iteration items="{!v.listControllingValues}" var="val">
                <option value="{!val}">{!val}</option>
            </aura:iteration>
        </lightning:select>
    </lightning:layoutItem>
    
    <!--Dependent Field-->
    <lightning:layoutItem size="12" padding="around-small">
        <lightning:select name="dependentFld"
                          value="{!v.objDetail.City__c}"
                          label="City"
                          disabled="{!v.bDisabledDependentFld}"
                          onchange="{!c.onSubControllerFieldChange}">
            <aura:iteration items="{!v.listDependingValues}" var="val">
                <option value="{!val}">{!val}</option>
            </aura:iteration>
        </lightning:select>
    </lightning:layoutItem>
    
    <!--sub Dependent Field-->
    <lightning:layoutItem size="12" padding="around-small">
        <lightning:select name="subDependentFld"
                          value="{!v.objDetail.Language__c}"
                          label="language"
                          disabled="{!v.bDisabledSubDependentFld}">
            <aura:iteration items="{!v.listSubDependingValues}" var="val">
                <option value="{!val}">{!val}</option>
            </aura:iteration>
        </lightning:select>
    </lightning:layoutItem>
    
</aura:component>

js controller
({
    doInit : function(component, event, helper) { 
        // get the fields API name and pass it to helper function  
        var controllingFieldAPI = component.get("v.controllingFieldAPI");
        var dependingFieldAPI = component.get("v.dependingFieldAPI");
        var subDependingFieldAPI = component.get("v.subDependingFieldAPI");
        
        var objDetails = component.get("v.objDetail");
        // call the helper function
        helper.fetchPicklistValues(component,objDetails,controllingFieldAPI, dependingFieldAPI, "v.depnedentFieldMap");
        
        // 2nd and 3ed picklist 
        helper.fetchPicklistValues(component,objDetails,dependingFieldAPI, subDependingFieldAPI, "v.subDepnedentFieldMap");
    },
    
    onControllerFieldChange: function(component, event, helper) {     
        var controllerValueKey = event.getSource().get("v.value"); // get selected controller field value
        var depnedentFieldMap = component.get("v.depnedentFieldMap");
        
        if (controllerValueKey != '--- None ---') {
            // disable and reset sub dependent field 
           
           
            var ListOfDependentFields = depnedentFieldMap[controllerValueKey];
            
            if(ListOfDependentFields.length > 0){
                component.set("v.bDisabledDependentFld" , false);  
                helper.fetchDepValues(component, ListOfDependentFields,"v.listDependingValues");    
            }else{
                component.set("v.bDisabledDependentFld" , true); 
                component.set("v.listDependingValues", ['--- None ---']);
            }  
            
        } else {
            component.set("v.listDependingValues", ['--- None ---']);
            component.set("v.bDisabledDependentFld" , true);
        }
        
        component.set("v.bDisabledSubDependentFld" , true);
        component.set("v.listSubDependingValues", ['--- None ---']);
    },
    
    
    onSubControllerFieldChange : function(component, event, helper) {     
        var controllerValueKey = event.getSource().get("v.value"); // get selected sub controller field value
        var depnedentFieldMap = component.get("v.subDepnedentFieldMap");
       
        if (controllerValueKey != '--- None ---') {
            var ListOfDependentFields = depnedentFieldMap[controllerValueKey];
            if(ListOfDependentFields.length > 0){
                component.set("v.bDisabledSubDependentFld" , false);  
                helper.fetchDepValues(component, ListOfDependentFields,"v.listSubDependingValues");    
            }else{
                component.set("v.bDisabledSubDependentFld" , true); 
                component.set("v.listSubDependingValues", ['--- None ---']);
            }  
            
        } else {
            component.set("v.listSubDependingValues", ['--- None ---']);
            component.set("v.bDisabledSubDependentFld" , true);
        }
    },
})

js helper :
({
    fetchPicklistValues: function(component,objDetails,controllerField, dependentField,mapAttrName) {
        // call the server side function  
        var action = component.get("c.getDependentMap");
        // pass paramerters [object definition , contrller field name ,dependent field name] -
        // to server side function 
        action.setParams({
            'objDetail' : objDetails,
            'contrfieldApiName': controllerField,
            'depfieldApiName': dependentField 
        });
        //set callback   
        action.setCallback(this, function(response) {
            if (response.getState() == "SUCCESS") {
                //store the return response from server (map<string,List<string>>)  
                var StoreResponse = response.getReturnValue();
                // once set #StoreResponse to depnedentFieldMap attribute 
                component.set(mapAttrName,StoreResponse);
                
                if(mapAttrName == 'v.depnedentFieldMap'){

                    // create a empty array for store map keys(@@--->which is controller picklist values) 
                    var listOfkeys = []; // for store all map keys (controller picklist values)
                    var ControllerField = []; // for store controller picklist value to set on lightning:select. 
                    
                    // play a for loop on Return map 
                    // and fill the all map key on listOfkeys variable.
                    for (var singlekey in StoreResponse) {
                        listOfkeys.push(singlekey);
                    }
                    
                    //set the controller field value for lightning:select
                    if (listOfkeys != undefined && listOfkeys.length > 0) {
                        ControllerField.push('--- None ---');
                    }
                    
                    for (var i = 0; i < listOfkeys.length; i++) {
                        ControllerField.push(listOfkeys[i]);
                    }  
                    // set the ControllerField variable values to country(controller picklist field)
                    component.set("v.listControllingValues", ControllerField);
                }
            }else{
                alert('Something went wrong..');
            }
        });
        $A.enqueueAction(action);
    },
    
    fetchDepValues: function(component, ListOfDependentFields,lstAttrName) {
        // create a empty array var for store dependent picklist values for controller field  
        var dependentFields = [];
        dependentFields.push('--- None ---');
        for (var i = 0; i < ListOfDependentFields.length; i++) {
            dependentFields.push(ListOfDependentFields[i]);
        }
        // set the dependentFields variable values to store(dependent picklist field) on lightning:select
        component.set(lstAttrName, dependentFields);
        
    },
    
})

No need to change in Apex ​Controller

User-added image

Thanks , let us know if it helps you 
http://sfdcMonkey.com
This was selected as the best answer
Andy PutraAndy Putra
Hi, I have some problem.
Can you share the class of dependentPicklist_UpdateCtrl?

Many thanks
sarah j d 4sarah j d 4
Hi Andy Putra,

FYI!

http://sfdcmonkey.com/2018/08/31/dependent-picklist-lightningselect-lightning-salesforce/

Regards
Sarah
Andrew Wallis 12Andrew Wallis 12
Hi all,

This has been really helpful, but I have one problem: when I call helper.fetchPickListValues more than once (as in the js controller code above lines 10-13), an error comes up saying the function does not exist - but only the second time! Has anybody else experienced this or have any ideas what is going on?