Dependent picklists in Lightning

A recurring problem I face in my lightning developments is the need to manage two or more picklists dependent on each other.

With this article we will see how you can easily develop a component able to render dependent picklist values, by leveraging the Lightning Application events.

We start by creating a new “DependentPicklist” component with a lightning:select that shows a list of available items. In this example we will use a simple list, but you can easily evolve it to handle a label/value list.

<aura:component >
    <aura:attribute name="id" type="String" />
    <aura:attribute name="selectedValue" type="String" />
    <aura:attribute name="values" type="String[]"/>
    <lightning:select name="{!v.id}" label="Select" value="{!v.selectedValue}">
<option value="">-- Select --</option>
        <aura:iteration items="{!v.values}" var="v">
<option value="{!v}" text="{!v}"></option>
        </aura:iteration>
    </lightning:select>
</aura:component>

Now let’s go deeply in the development. Our picklist will show values based on other picklists’ selection, so when a picklist changes its value, we need to notify all the picklist to adequate their values. We need an “Application” event.

<aura:event type="APPLICATION" description="A picklist value is selected">
    <aura:attribute name="Id" type="String" description="Identifier of the picklist"/>
    <aura:attribute name="SelectedValue" type="String" description="The new value of the picklist"/>
</aura:event>

The event will contain the identifier of the picklist changed, as well as the selected value.
Our component will therefore have to handle this event and react accordingly, so we define our handler function.

    <aura:handler event="c:PicklistValueChange" action="{!c.handleParentValueChange}"/>

Now, the trick to find out what values should be rendered, and that is stored in a data structure.
We use an attribute to know all the values and their dependencies. The attribute value will be a JSON string like this:

[{value:’child1′,dependsOn:’master#value1′},
{value:’child2′,dependsOn:’master#value2′},
{value:’child3′,dependsOn:’master#value3′}]

In this way, we can found if a value depends on another value of a picklist. Of course, during the component initialisation we’ll show only free values, those that don’t depend on other picklists.

doInit : function(component, event, helper) {
    var dependencyStructure = component.get("v.dependencyStructure");
    var values = component.get("v.values") || [];
    for(var i in dependencyStructure){
        if(!dependencyStructure[i].dependsOn || dependencyStructure[i].dependsOn == "") {
            values.push(dependencyStructure[i].value);
        }
    }
    component.set("v.values", values);
},

Returning to our event, we still have to provide a proper reaction to the event, to recalculate the items to show.
We’ve to read the dependency structure, as the picklist in the event could be a “master”, by looping in the list looking for the picklist being changed and the specific (modified) value.

We verify the dependencies and – just in case – add or remove the available values from our current picklist.

handleParentValueChange : function(component, event, helper) {
    var masterId = event.getParam("Id");
    var masterValue = event.getParam("SelectedValue");
    var dependencyStructure = component.get("v.dependencyStructure");
    var values = component.get("v.values") || [];
    for(var i in dependencyStructure) {
        if(dependencyStructure[i].dependsOn) {
            var dep = dependencyStructure[i].dependsOn.split("#");
            if(dep[0] == masterId && dep[1] == masterValue) {
                values.push(dependencyStructure[i].value);
            } else if(dep[0] == masterId && values.indexOf(dependencyStructure[i].value)>-1) {
                values.splice(values.indexOf(dependencyStructure[i].value), 1);
            } else {
                //do nothing
            }
        }
    }
    component.set("v.values", values);
},

Finally, who deals with invoking the event?
We do not like to exaggerate with the definition of new metadata, so we’re going to reuse the same component, that will be both an invocator and an handler of the event. Returning to the code, we just need to fire the event upon value change.

handleValueChange: function(component, event, helper) {
    var id = component.get("v.id");
    var selectedValue = component.get("v.selectedValue");
    var appEvent = $A.get("e.c:PicklistValueChange");
    appEvent.setParams({ "Id" : id });
    appEvent.setParams({ "SelectedValue" : selectedValue });
    appEvent.fire();
}

And that’s all. Now, you have an advanced component for picklist management.

<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome">
    <lightning:card>
        <c:DependentPicklist id="master" dependencyStructure="[{value:'value1'},{value:'value2'},{value:'value3'}]"/>
        <c:DependentPicklist id="dependent" master="master" dependencyStructure="[{value:'child1',dependsOn:'master#value1'},{value:'child2',dependsOn:'master#value2'},{value:'child3',dependsOn:'master#value3'}]"/>
    </lightning:card>
</aura:component>   

This is an example:

Yeah baby

Leave a Reply

Your email address will not be published. Required fields are marked *