Saturday, October 26, 2019

Custom Dependent Picklist Field in Salesforce using Lightning Web Components

This post explains how to implement custom dependent picklist field using lightning web components(lwc)



To get the picklist values in lightning web components we can use 'getPicklistValues' or 'getPicklistValuesByRecordType' wire adapters, these wire adapters uses the lightning/uiObjectInfoApi scoped module to get the picklist values based on specific Field or based on Record Type.

Syntax

import { getPicklistValues, getPicklistValuesByRecordType } from 'lightning/uiObjectInfoApi';

For the demo, I created two fields on Account.
Controlling Field(picklist): Account Country
Dependent Field(picklist): Account State

The structure of the dependent field


HTML Code
<template>
    <lightning-card title="Custom Dependent Picklist using Lightning Web Components"><br/>
        <div class="slds-grid slds-gutters" style="margin-left:3%">
            <div class="slds-col slds-size_1-of-4">
                <lightning-combobox label="Account Country" 
                                    name="country" 
                                    onchange={handleCountryChange} 
                                    options={controllingValues} 
                                    placeholder="--None--" 
                                    value={selectedCountry}></lightning-combobox><br/>
                
                <div if:true={selectedCountry}>
                    Selected Country: <b>{selectedCountry}</b>
                </div>
            </div>
            <div class="slds-col slds-size_1-of-4">
                <lightning-combobox label="Account State" 
                                    name="state"
                                    onchange={handleStateChange} 
                                    options={dependentValues} 
                                    placeholder="--None--" 
                                    value={selectedState}
                                    disabled={isEmpty}></lightning-combobox><br/>
                <div if:true={selectedState}>
                    Selected State: <b>{selectedState}</b>
                </div>
            </div>
        </div><br/>

        <div if:true={error}>
            <p>{error}</p>
        </div>
    </lightning-card>
</template>

Javascript Controller Code
import { LightningElement, wire, track } from 'lwc';
import { getPicklistValuesByRecordType } from 'lightning/uiObjectInfoApi';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';

export default class DependentPickListInLWC extends LightningElement {

    // Reactive variables
    @track controllingValues = [];
    @track dependentValues = [];
    @track selectedCountry;
    @track selectedState;
    @track isEmpty = false;
    @track error;
    controlValues;
    totalDependentValues = [];

    // Account object info
    @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
    objectInfo;

    // Picklist values based on record type
    @wire(getPicklistValuesByRecordType, { objectApiName: ACCOUNT_OBJECT, recordTypeId: '$objectInfo.data.defaultRecordTypeId'})
    countryPicklistValues({error, data}) {
        if(data) {
            this.error = null;

            let countyOptions = [{label:'--None--', value:'--None--'}];

            // Account Country Control Field Picklist values
            data.picklistFieldValues.Account_Country__c.values.forEach(key => {
                countyOptions.push({
                    label : key.label,
                    value: key.value
                })
            });

            this.controllingValues = countyOptions;

            let stateOptions = [{label:'--None--', value:'--None--'}];

             // Account State Control Field Picklist values
            this.controlValues = data.picklistFieldValues.Account_State__c.controllerValues;
            // Account State dependent Field Picklist values
            this.totalDependentValues = data.picklistFieldValues.Account_State__c.values;

            this.totalDependentValues.forEach(key => {
                stateOptions.push({
                    label : key.label,
                    value: key.value
                })
            });

            this.dependentValues = stateOptions;
        }
        else if(error) {
            this.error = JSON.stringify(error);
        }
    }

    handleCountryChange(event) {
        // Selected Country Value
        this.selectedCountry = event.target.value;
        this.isEmpty = false;
        let dependValues = [];

        if(this.selectedCountry) {
            // if Selected country is none returns nothing
            if(this.selectedCountry === '--None--') {
                this.isEmpty = true;
                dependValues = [{label:'--None--', value:'--None--'}];
                this.selectedCountry = null;
                this.selectedState = null;
                return;
            }

            // filter the total dependent values based on selected country value 
            this.totalDependentValues.forEach(conValues => {
                if(conValues.validFor[0] === this.controlValues[this.selectedCountry]) {
                    dependValues.push({
                        label: conValues.label,
                        value: conValues.value
                    })
                }
            })

            this.dependentValues = dependValues;
        }
    }

    handleStateChange(event) {
        this.selectedState = event.target.value;
    }
}

Configuration File
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="dependentPickListInLWC">
    <apiVersion>46.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

You can also find the code on Github
GitHub Link


Result

11 comments:

  1. Replies
    1. Hi amit,
      I tested in my org it working as excepted to me, what is the issue you are facing.

      Delete
    2. HI
      please let me know whether this can be implemented in case of adding rows of dependent picklist. I am really getting this issue. please help
      Thanks in advance

      Delete
  2. It is working for me.But How to achieve same functionality for another picklist i have.country ,state ,city.I tried but iam unable to get values in 3picklist if you have any idea please tell me.

    ReplyDelete
    Replies
    1. It works for another Picklist field as well as i have achieved for another one

      Delete
  3. ----> conValues.validFor[0] <----

    There is a bug with this line. How is this going to work? Some dependent values can be used across different controlling values. This is where your code will break.

    For example:

    Controlling: Type
    Depdendent: Sub_Type__c

    Type Picklist: Type [ val1, val2, val3]
    Sub Type Picklist: Sub_Type__c [Subval1, Subval2, Subval3]

    Depdendency: val1: [Subval1, Subval3]
    val2: [no depdenent value]
    val3: [Subval2, Subval3]

    This line --> conValues.validFor[0] when executed will be pick on the first value from array. When the user selects val3 from Type picklist, there won't be any dependent value available.

    Subva1 is Validfor [1]
    Subval2 is Validfor [3]
    Subval3 is Validfor [1,3]

    Try this scenario and see if this works for you.

    ReplyDelete
  4. In short this will only work where 1 dependent value is not being used against another controlling value. Since in your example states will be always against 1 country so they won't be used against other countries.

    ReplyDelete
    Replies
    1. yes it works for only 1 dependent value it will not work for multiple control values

      Delete
    2. It can work for above given situation also, all you need to place another forEach loop for validFor instead of giving [0]

      Delete
  5. Hi,

    I am not able to implement this. it is showing undefined value for data.picklistFieldValues when using this statement--> this.totalDependentValues = data.picklistFieldValues.MailingState.values;

    ReplyDelete