+ Start a Discussion
Francis A.Francis A. 

Alternate implementation of dependent picklists inside an aura:iteration in Lightning

I'm looking at alternate ways of implementing dependent picklists that are within an aura:iteration. Basic processing is that I am displaying a list of records with a couple of fields rendered as input (the controlling and dependent picklist). Unfortunately, this did not work for me and I've tried:
  1. lightning:recordEditForm - The form renders the fields vertically and both picklists need to be rendered at the same time, which is not the case for an iterating list of records and fields.
  2. ui:inputselect, but hit a snag where aura:id can not take in dynamically generated values and therefore the controller does not have any way to determine what specific inputselect tag in the list of records it will try to rerender.
Please see code sample below:
 
<aura:iteration items="{!v.displayDTORecord}" var="item">
<tr class="slds-hint-parent">
    <aura:iteration items="{!item.lstFieldVal}" var="f">
        <aura:if isTrue="{!f.field_api == 'Name'}">
            <td role="gridcell" data-label="{!f.field_api}">
                <div class="slds-truncate" title="{!f.value}">
                    <a href="javascript:void(0);" id="{!item.recId}" onclick="{!c.doRedirect}"><span>{!f.value}</span></a>
                </div>
            </td> 
        <aura:set attribute="else">
            <td role="gridcell" data-label="{!f.field_api}">
                <aura:if isTrue="{!f.field_api != 'Additional_Details__c'}">
                    <aura:if isTrue="{!f.inputField == false}">
                        <div class="no-text-overflow slds-truncate" title="{!f.value}">{!f.value}</div>
                    <aura:set attribute="else">

                        <aura:if isTrue="{!f.field_api == 'Offer_Status__c'}">
            <!--            
                            <div class="slds-form-element">
                                <div class="slds-select_container">
                                    <ui:inputSelect aura:id="{!f.field_api_idx}" change="{!c.onControllerTOFieldChange}" value="{!f.value}"/>
                                </div>
                            </div>
            -->
                            <lightning:select name="mySelect" label="" aura:id="{!f.field_api_idx}" value="{!f.value}">
                                 <aura:iteration items="{!v.options}" var="item">
                                    <option text="{!item.label}" value="{!item.value}" selected="{!item.selected}"/>
                                </aura:iteration>
                            </lightning:select>
                            <aura:set attribute="else">
                                <div class="slds-form-element">
                                    <div class="slds-select_container">
                                        <ui:inputSelect aura:id="Offer_Outcome__c" disabled="{!v.isDependentDisableTO}" change="{!c.onDependentTOFieldChange}" value="{!f.value}" />
                                    </div>
                                </div>
                            </aura:set>
                        </aura:if>                                                                          
                    </aura:set>
                </aura:if>
Is there a workaround or something that you would've similarly implemented successfully? Thanks.
Alain CabonAlain Cabon
Hi,

An alternative to <lightning:inputField>  ?

https://developer.salesforce.com/docs/component-library/bundle/lightning:inputField/documentation


Dependent Picklist Example:

This example uses LeadSource as the controlling field and Level__c as the dependent field for a dependent picklist.
 
<lightning:recordEditForm aura:id="recordViewForm" 
                                  recordId="003R00000000000000"
                                  recordTypeId="012R00000000000000"
                                  objectApiName="Contact">
    <lightning:messages />
    <!--Other fields here-->
    <lightning:inputField fieldName="LeadSource" />
    <lightning:inputField fieldName="Level__c" />
    <lightning:button aura:id="submit" type="submit" label="Update record" class="slds-m-top_medium"/>
</lightning:recordEditForm>

<lightning:inputField> is not as easy to use as <apex:inputField> for VFP (many understandable constraints).

https://success.salesforce.com/ideaView?id=08730000000Dom1AAC

 
Francis A.Francis A.
Hi Alain, tried that as well, but doesn't work for this use case. Thanks.
Alain CabonAlain Cabon

1) This famous blog tried to solve many missing features of LEX based here on the real metadata of the dependant picklists but it is difficult to do completely (read the comments at the end of the page for the recent bugs). Even for Salesforce, it is difficult (<lightning:inputField> is recent and still difficult to use)

 http://sfdcmonkey.com/2017/02/18/dependent-picklist-fields-lightning-component/


2) An easy smart alternative if you don't use the standard dependant picklists of Salesforce but your own lists (here Country, Citiy)
The code is short and works fine (all is done by the structure of the lists and the single event onchange="{!c.PickChange}"​

http://www.minerva18.com/blog/creating-dependent-picklists-in-lightning-components/

There are many alternatives on the internet for the dependant picklists wiht jQuery and so on.
Francis A.Francis A.
These are relevant solutions to straightforward dependent picklist implementations ie. no record iteration. I haven't delved with JQuery or JS alternatives as I'm wary of LockerService and the maintainability of those solutions. Do you have some examples anyway? I have yet to come across something that will work in my scenario.  Thanks again. 
Alain CabonAlain Cabon
No one else will help you here because you don't have post your JS controller that doesn't work (probably a short piece code).

Your component is not enough if you want some help.

It is your JS controller that is not correct and it is the interesting part that we would like to see.
Francis A.Francis A.
No worries and thanks for looking into this Alain!

Controller
 
doInit: function(component, event, helper){

    helper.fetchTOPicklistValues(component, 'Offer_Status__c', 'Offer_Outcome__c');

},  

// function call onchange controller field  
onControllerTOFieldChange: function(component, event, helper) {

    // get the selected value
    var controllerValueKey = event.getSource().get("v.value");

    // get the map values   
    var Map = component.get("v.dependentFieldMap");

    // check if selected value is not equal to None then call the helper function.
    // if controller field value is none then make dependent field value is none and disable field
    if (controllerValueKey != '-- None --') {

        // get dependent values for controller field by using map[key].  
        var ListOfDependentFields = Map[controllerValueKey];
        helper.fetchDepValuesType(component, ListOfDependentFields);

    } else {
        var defaultVal = [{
        class: "optionClass",
        label: '-- None --',
        value: '-- None --'
        }];
        component.find('Offer_Outcome__c').set("v.options", defaultVal);
        component.set("v.isDependentDisableTO", false);

    }
},

onDependentTOFieldChange: function(component, event, helper) {

    var onDep = event.getSource().get("v.value");

},

Helper
 
fetchTOPicklistValues: function(component, controllerField, dependentField) {

      // call the server side function  
      var action = component.get("c.getFieldDependencies");
      // pass parameters [object name, controller field name, dependent field name]
      // to server side function 

      action.setParams({
         'objApiName': component.get("v.objTOInfo"),
         '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 dependentFieldMap attribute 
            component.set("v.dependentFieldMap", StoreResponse);

            // 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 ui field. 

            // 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 ui:inputSelect  
            if (listOfkeys != undefined && listOfkeys.length > 0) {

               ControllerField.push({
                  class: "optionClass",
                  label: "-- None --",
                  value: "-- None --"
               });

            }

            for (var i = 0; i < listOfkeys.length; i++) {

               ControllerField.push({
                  class: "optionClass",
                  label: listOfkeys[i],
                  value: listOfkeys[i]
               });

            }

            // set the ControllerField variable values to controller picklist field
            component.find("Offer_Status__c0").set("v.options", ControllerField);
         } else {

            console.log("ERROR : ", response);
            var errors = action.getError();
            console.log('ERROR Val : ', response.getError());

         }
      });
      $A.enqueueAction(action);
   },

My problems are: 1) looking at the ui:inputSelect(s), I am unable to reference an index in aura:id that I am able to component.find back in the controller to pass back the picklist values. 2) I believe same thing will happen to the lightning:select because I will still need to reference an aura:id.
So I am stuck, but is there a different way to implement this if I did hit an aura:id limitation?