[SalesForce] Working with Lightning comboBox, UI:input component and aura handler change event

I have a sample code which is independent of any data model for now. I am trying to display data from a JSON using <Aura:Iteration>. I have below two issues with my code:

  1. I do not see the value in <lightning:combobox> when I load the component for the first time. I am expecting to see Achme , John and Mark already populated in the combox. However I see the component like below. I seem to be missing something.

enter image description here

  1. How do I make the selected option from lightning comboBox update the DataList row value. For example if I select a new value – ABC from lightning combox , how can I update that value in the dataList.

  2. I have a change event on this component. However this change event is not detecting any change to the dataList change. Example when I change the Age value of one of the row, the change event does not trigger. I must be using it incorrectly mostly.

Here is the component:

<aura:component implements="lightning:availableForFlowScreens,force:appHostable">
<aura:attribute name="dataList" type="List"/>
    <aura:handler name="change" value="{!v.dataList}" action="{!c.handleDataListChange}"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <div class="slds-is-relative slds-scrollable" >
        <!-- MARKUP for the datatable-->
        <table class="slds-table slds-table--bordered " role="grid">
            <thead>
            <tr>
                <th class="slds-is-sortable slds-is-resizable slds-text-title_caps" scope="col">
                    <span class="slds-truncate" title="SegmentChannelName">Name</span>
                    <span class="slds-assistive-text" aria-live="assertive" aria-atomic="true"></span>
                </th>
                <th class="slds-is-sortable slds-is-resizable slds-text-title_caps" scope="col">
                    <span class="slds-truncate" title="Creative">Age</span>
                    <span class="slds-assistive-text" aria-live="assertive" aria-atomic="true"></span>
                </th>
                <th class="slds-is-sortable slds-is-resizable slds-text-title_caps" scope="col">
                    <span class="slds-truncate" title="Channel Start">State</span>
                    <span class="slds-assistive-text" aria-live="assertive" aria-atomic="true"></span>
                </th>
            </tr>
            </thead> 
            <tbody>
                <aura:iteration items="{!v.dataList}" var="row" indexVar="index">
                    <tr onchange="{!c.handleRowChange}">
                        <td role="gridcell">
                            <lightning:combobox aura:id="picklist"
                                                label="Names"
                                                required="true"
                                                variant="label-hidden"
                                                value="{!row.data.Name}"
                                                options="{!row.Names}"/>
                        </td>
                        <td role="gridcell">
                            <ui:inputText class="slds-cell-wrap" value="{!row.data.Age}"/>
                        </td>
                        <td role="gridcell">
                            <ui:inputText class="slds-cell-wrap" value="{!row.data.State}"/>
                        </td> 
                        <td role="gridcell">
                            <ui:inputDate displayDatePicker="true"
                                          class="slds-input"
                                          disabled="false"
                                          updateOn = "change"
                                          value= "{!row.data.Date}"
                                          format="MM/dd/yyyy"/>
                        </td>
                    </tr>
                </aura:iteration>
            </tbody>
        </table>
    </div>

</aura:component>

Controller:

({
     doInit : function(component, event, helper) {
         const json = '[{"data": {"Name": "Acme","Age": "34","State": "TX","Date" : "2020-01-24"},"Names": [{"label": "Acme","value": "Acme"},{"label": "ABC","value": "abc"},{"label": "XYZ","value": "XYZ"}],"RecordTypeName": "Manager"},{"data": {"Name": "John","Age": "21","State": "NY","Date" : "2020-01-24"},"Names": [{"label": "MNM","value": "MNM"},{"label": "TTT","value": "TTT"},{"label": "John","value": "John"}],"RecordTypeName": "Employee"},{"data": {"Name": "Mark","Age": "23","State": "CA","Date" : "2020-01-24"},"Names": [{"label": "Mark","value": "Mark"},{"label": "CD","value": "CD"},{"label": "BD","value": "BD"}],"RecordTypeName": "Employee"}]';
         const obj = JSON.parse(json);
         component.set('v.dataList', obj);
         console.log('dataList === ' + JSON.stringify(component.get('v.dataList')));
     },
    handleDataListChange : function(component, event, helper) {
        console.log('handleDataListChange ==== > ');
    },
    handleRowChange : function(component, event, helper) {
        console.log('handleRowChange ==== > ');
    }    
})

Update:

Based on the answer I update my code and was able to find the solution for point 1 and 2.

I have a new question related invoking the OnChange attribute- I introduced a new Date field in the datalist column. My issue now is the handleRowChange event is not triggering when I change the dates. However it trigger when I change the comboBox or a text field. I tried using updateOn attribute for ui:inputDate however that did not help.

Is there a way to invoke the onChange handler method- handleRowChange when I change the value in ui:inputDate?

enter image description here

Best Answer

  1. Your not setting the value attribute for your options, it should be below format

       [{ value: "abc", label: "ABC" },{ value: "xyz", label: "XYZ" }]
    
  2. your Name value is not existing in to your options that's why it's not selected default, in your below Json data Acme is not existing options(Acme not equal abc/xyz).

    {
    "data": {"Name": "Acme","Age": "34","State": "TX"},  //Acme is 
    "Names": [{"label": "ABC"},{"label": "XYZ"}],
    "RecordTypeName": "Manager"
    }
    
  3. i would recommend go through lightning combo box , you need to use onchange event like like onchange="{! c.handleChange }" to update the datalist with selected value.

  4. ---Update with onchange Event-- i don't recommenced to use UI controls because UI name space going to be retired in summer 21 . i added onchange event componenet level not in tr level. here is tested code befor change

after change after change

cmp:

<aura:attribute name="dataList" type="List"/>
    <aura:attribute name="changedrow" type="List"/>
    <!--<aura:handler name="change" value="{!v.dataList}" action="{!c.handleDataListChange}"/>-->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <div class="slds-is-relative slds-scrollable" >
        <!-- MARKUP for the datatable-->
        <table class="slds-table slds-table--bordered " role="grid">
            <thead>
            <tr>
                <th class="slds-is-sortable slds-is-resizable slds-text-title_caps" scope="col">
                    <span class="slds-truncate" title="SegmentChannelName">Name</span>
                    <span class="slds-assistive-text" aria-live="assertive" aria-atomic="true"></span>
                </th>
                <th class="slds-is-sortable slds-is-resizable slds-text-title_caps" scope="col">
                    <span class="slds-truncate" title="Creative">Age</span>
                    <span class="slds-assistive-text" aria-live="assertive" aria-atomic="true"></span>
                </th>
                <th class="slds-is-sortable slds-is-resizable slds-text-title_caps" scope="col">
                    <span class="slds-truncate" title="Channel Start">State</span>
                    <span class="slds-assistive-text" aria-live="assertive" aria-atomic="true"></span>
                </th>
            </tr>
            </thead> 
            <tbody>
                <aura:iteration items="{!v.dataList}" var="row" indexVar="index">
                    <tr>
                        <td role="gridcell">
                            <lightning:combobox aura:id="Nameid"
                                                label="Names"
                                                required="true"
                                                name="{!'' +'Nameid=>'+index}"
                                                variant="label-hidden"
                                                value="{!row.data.Name}"
                                                options="{!row.Names}"
                                                onchange="{!c.rowaction}"/>
                        </td>
                        <td role="gridcell">
                            <lightning:input aura:id="ageid" class="slds-cell-wrap" name="{!'' +'Ageid=>'+index}" value="{!row.data.Age}" onchange="{!c.rowaction}"/>
                        </td>
                        <td role="gridcell">
                            <lightning:input class="slds-cell-wrap" aura:id="stateid" name="{!'' +'Stateid=>'+index}" value="{!row.data.State}" onchange="{!c.rowaction}"/>
                        </td>
                    </tr>
                </aura:iteration>
            </tbody>
        </table>

        <lightning:card title="changed row">
       <aura:iteration items="{!v.changedrow}" var="row" indexVar="index">
           <p><lightning:formattedText value="{!row.data.Name}"/></p>
           <p><lightning:formattedText value="{!row.data.Age}"/></p>
           <p><lightning:formattedText value="{!row.data.State}"/></p>
            </aura:iteration>

    </lightning:card>
    </div>

controller:

 doInit : function(component, event, helper) {
    const json = '[{"data": {"Name": "Acme","Age": "34","State": "TX"},"Names": [{"label": "Acme","value": "Acme"},{"label": "ABC","value": "abc"},{"label": "XYZ","value": "XYZ"}],"RecordTypeName": "Manager"},{"data": {"Name": "John","Age": "21","State": "NY"},"Names": [{"label": "MNM","value": "MNM"},{"label": "TTT","value": "TTT"},{"label": "John","value": "John"}],"RecordTypeName": "Employee"},{"data": {"Name": "Mark","Age": "23","State": "CA"},"Names": [{"label": "Mark","value": "Mark"},{"label": "CD","value": "CD"},{"label": "BD","value": "BD"}],"RecordTypeName": "Employee"}]';
    const obj = JSON.parse(json);
    component.set('v.dataList', obj);
    console.log('dataList === ' + JSON.stringify(component.get('v.dataList')));
},
rowaction: function(component, event, helper){
    var index = event.getSource().get("v.name");
    var idAndindex = event.getSource().get("v.name").split("=>"),
        datalist = component.get('v.dataList'),
        changedvalue = event.getSource().get("v.value"),
        currentrow = datalist[idAndindex[1]];
    switch (idAndindex[0]){
        case 'Nameid':
            currentrow.Name = changedvalue;
            break;
        case 'Ageid':
            currentrow.Age = changedvalue;
            break;
        case 'Stateid':
            currentrow.State = changedvalue;
            break;
    }
    var changedrows = [].concat(currentrow);
    component.set("v.changedrow",changedrows);
    datalist.splice(idAndindex[1] ,1, currentrow);

    component.set("v.dataList",datalist);

},