11

Trying to populate an lightning attribute if a single record exists and if multiple records exists would like to provide the user a select list to choose the record then set the attribute.

I cannot seem to get the lightning:select to get its value to be set:

Component

<aura:component description="Account_Selection" controller="MyController">
        <!-- Handle component initialization in a client-side controller -->
        <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

        <!-- The list of accounts to display -->
        <aura:attribute name="accounts" type="Account[]"/>

        <lightning:select aura:id="a_opt" name="a_opt" label="Accounts"  onchange="{!c.onSelectChange}">
            <aura:iteration items="{!v.accounts}" var="account">
                <option value="{!account.Id}">{!account.Name}</option>
            </aura:iteration>
        </lightning:select>
</aura:component>

Controller

({
    doInit : function(component, event, helper) {
        //console.log('here');
        helper.getAccounts(component);
    },

    onSelectChange : function(component, event, helper) {
        // Print out the selected value
        var selected = component.find("a_opt").get("v.value");
        console.log('@@@@@@@: ' + selected);
    }
})

Helper

({
    getAccounts : function(cmp) {
         var action = cmp.get("c.getAccounts");
         action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                cmp.set("v.accounts", response.getReturnValue());
                cmp.set("v.accountId","ANIDTOTESTTHESET");
                var sel = cmp.find('a_opt');
                sel.set("v.value","ANIDTOTESTSETTINGTHEVALUE");
            }
        });
         $A.enqueueAction(action);
    }
})

Now, the select DOES show the available select options but I cannot for the life of me get the value to be set via the code.

The above component is within a parent component as so:

<c:Account_Selection aura:id="a_selection"/>

The parent component has an attribute

<aura:attribute name="accountId" type="Id"/>

What I would like to happen:

  1. If only one account record returned from the apex controller, set the value of the accountId attribute to the Id of that record
  2. If multiple accounts found, set the default value of the select list to a specific value and set the attribute value
  3. If user selects a value then set the value of the attribute

I have tried everything I can think of searched but cannot get it to work. The select list has all the options but the first value seems to always be selected regardless of the value I attempt to set.

The outPutText does not have any value despite explicitly setting it from the component..

Any ideas how to make this work?

Update

I think the issue is the "cmp" is not the lightning component it is the apex controller? How do i get a reference back to the component on the callback?

  • why are you manually trying to select using cmp.find() instead of using value attribute in the lightning:select point to v.accountId? – Praveen Jan 6 '17 at 19:43
  • I think cmp is a reference to the component. Are you having the problem in the getAccountss helper function? (is the addition s a typo?) – Phil Hawthorn Jan 6 '17 at 19:44
  • Assuming the account Id is a valid, 18-character ID, it should work. If you're using just 15-character ID values, it won't work. Does that help? – sfdcfox Jan 6 '17 at 19:56
  • Solved it... One sec. – sfdcfox Jan 6 '17 at 20:06
  • 1
    @Praveen - I will see how well that works. I am essentially trying to default the value if only one option and allow select if multiple options. If only one option the select will be hidden. As to the Why, I am still learning lol so seeing how everything works in relation to the lifecycle....This is not your momma's visualforce anymore. – Eric Jan 6 '17 at 20:28
13

Setting the options doesn't actually cause the rendering to happen. So, the value you're setting is ignored because it isn't valid at the time. The solution is to use another callback post-rendering. Here's an example app I wrote that demonstrates this:


<aura:application extends="force:slds" controller="LightningAccountController">
    <aura:attribute name="accounts" type="Account[]" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <lightning:select label="Choose 1" name="a_opt" aura:id="a_opt" onchange="{!c.change}">
        <aura:iteration items="{!v.accounts}" var="account">
            <option value="{!account.Id}">{!account.Name}</option>
        </aura:iteration>
    </lightning:select>
</aura:application>

// Controller
({
    doInit: function(component, event, helper) {
        // Request from server
        var action = component.get("c.getAccountList");
        action.setCallback(this, function(result){
            var accounts = result.getReturnValue();
            console.log(accounts);
            component.set("v.accounts", accounts);
            // Let DOM state catch up.
            window.setTimeout(
                $A.getCallback( function() {
                    // Now set our preferred value
                    component.find("a_opt").set("v.value", accounts[4].Id);
                }));
        });
        $A.enqueueAction(action);
    }
})

// Apex class
global class LightningAccountController {
    @AuraEnabled global static Account[] getAccountList() {
        return [SELECT Name FROM Account LIMIT 10];
    }
}
  • I had an inkling it was a rendering issue. Will try it out later this evening but I am sure it will work...Thank you very much! – Eric Jan 6 '17 at 20:10
  • @Eric It's my pleasure. I learned something new today as well. – sfdcfox Jan 6 '17 at 20:12
  • It is wierd because if I hardcode the values and do not do the enque action it works as expected. If you look at the example here (replacing the UI with Lightning NS): developer.salesforce.com/docs/atlas.en-us.lightning.meta/… - it works as expected, but if I pass in the sObject List and call the "loadOptions" from the callback and modify to use the sObjectList it does not set the value. Weird.....If you want me to update question with this example let me know. – Eric Jan 6 '17 at 20:13
  • is the settimeout part a "guess" or could dom delays or something affect this guess and cause it to not set the value? And can you say frustrated. About ready to drop all this multiple components thing as setting values on the parent component from the child takes more code than just placing the child component code directly on the parent.....maybe I am missing something. – Eric Jan 6 '17 at 20:42
  • 1
    @Eric I get what you mean... Somehow, the callback seems to need time to do rendering. I think it's a limitation of callbacks, although the docs don't seem to explicitly say that. See Modifying Components Outside the Framework Lifecycle, which was the inspiration/source of my answer. – sfdcfox Jan 6 '17 at 22:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.