[SalesForce] Lightning Component : ui:inputSelectOption : When Default value is set dynamically, user can’t change Picklist value

I am working on a Multi-Location Case creation component for our community site. The basic premise is that we use Account Contact Relationships to relate a single User/Contact to multiple accounts.

The lightning component queries an Apex Controller to get the list of relationships for the user and builds a Picklist (ui:inputSelectOption) dynamically using the response data. This worked great and generated a picklist with all of the options to choose from.

However, we elected to take it a step further and allow the user to select and store their "Default Support Account Id" in a custom field on the Contact object. So, now what we're trying to do is check if the User has a default set on their contact already, and if so, set the ui:inputSelectOption default value to that account on page load so they don't need to do it every time.

Issue: The default account is being set correctly by comparing the AccountId to the DefaultAccountId variable while iterating through the
Relationships, but the issue is that this seems to stick the picklist
into a permanently fixed position, and the user cannot change the
value anymore.


For example: If the default Account Id was 0010m00000GpdAtSZJ which is Test Account 1 and it matches to one of the AccountIds in
the iteration, it sets the value parameter for that
ui:inputSelectOption to true, thereby setting Test Account 1 as
the active/default value.

If the user attempts to change Test Account 1 to Test Account 2,
the picklist still says Test Account 1.


Ultimate question: How do I set a default value for a picklist (ui:inputSelectOption) on page load, but still allow the user to
modify it?


Code

Component:

<aura:component implements="forceCommunity:availableForAllPageTypes,force:hasRecordId" access="global" controller="CreateTicketController">
    <aura:attribute name="Relationships" type="AccountContactRelation" />
    <aura:attribute name="DefaultAccountId" type="Id" />
    <aura:attribute name="HasDefaultAccount" type="Boolean" default="false" />
    <aura:handler name="init" value="{!this}" action="{!c.init}" />

    <div class="slds-grid slds-gutters slds-wrap">
        <div class="slds-col slds-size_1-of-1 slds-medium-size_2-of-3 slds-large-size_2-of-3">
            <div class="slds-form-element">
                <label class="slds-form-element__label slds-text-heading_small" for="select-01">Select Account</label>
                <div class="slds-select_container">
                    <ui:inputSelect aura:id="" class="slds-select" change="">
                        <ui:inputSelectOption text="" label="" value="{!!v.HasDefaultAccount}" />
                        <aura:iteration items="{!v.Relationships}" var="option">
                            <ui:inputSelectOption text="{!option.AccountId}" label="{!option.Account.Name}" value="{!option.AccountId == v.DefaultAccountId}" />
                        </aura:iteration>
                    </ui:inputSelect>
                </div>
            </div>
        </div>
    </div>
</aura:component>

JS Controller:

({
    init: function (component, event, helper) {
        var action = component.get("c.getRelatedAccounts");
        var recordId = component.get("v.recordId");
        var userId = $A.get('$SObjectType.CurrentUser.Id');
        var relationships;
        var aloha = '/one/one.app#/alohaRedirect/apex';
        var opt = new Array();
        var defaultAccountId;

        action.setParams({
            "userId": userId
        });
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(component.isValid() && state == "SUCCESS"){
                relationships = JSON.parse(response.getReturnValue());
                defaultAccountId = relationships[0].Contact.Default_Support_AccountId__c;
                component.set("v.Relationships", relationships);

                if(defaultAccountId){
                    component.set("v.DefaultAccountId", defaultAccountId);
                    component.set("v.HasDefaultAccount",true);
                    console.log('Default ID: ' + defaultAccountId);
                }
            }
        });
        // Invoke the service
        $A.enqueueAction(action);
    }
})

Apex Controller:

public without sharing class CreateTicketController  {

    @AuraEnabled
    public static String getRelatedAccounts(Id userId){
        String recordJSON;
        Id contactId;
        try{
        contactId = [SELECT ContactId FROM User WHERE Id =: userId].ContactId;
        List<AccountContactRelation> accountRelations = [SELECT Id,AccountId,Account.Name,ContactId,Contact.Default_Support_AccountId__c FROM AccountContactRelation WHERE ContactId =: contactId];
        recordJSON = JSON.serialize(accountRelations);
        return recordJSON;
        }catch(Exception ex){
            return '{"error": "' + ex.getMessage() + '"}';
        }
    }
}

Best Answer

When the expression is reevaluated, the picklist is changing back to the previous value. This happens because changing a value causes an aura:valueChange that ultimately ends up causing the settings to go back to default. Instead, you will want to set the selected value on the ui:inputSelect directly. Because of the "rendering cycle," you should set the picklist value asynchronously after setting the options for the list.

Here's a demonstration about how to properly set the picklist values and the default value:

<aura:application >
    <aura:attribute name="sel" type="String" />
    <aura:attribute name="opts" type="List" />

    <aura:handler name="init" value="{!this}" action="{!c.init}" />

    <ui:inputSelect value="{!v.sel}" label="Demo">
        <aura:iteration items="{!v.opts}" var="opt">
            <ui:inputSelectOption text="{!opt}" value="{!opt}" />
        </aura:iteration>
    </ui:inputSelect>
</aura:application>

({
    init: function(component, event, helper) {
        setTimeout(
            $A.getCallback(
                function() {
                    component.set("v.sel", "there");
                }
            )
        );
        component.set("v.opts", ["hello","there","world"]);
    }
})

Note the use of $A.getCallback to make sure the value change occurs, and the setTimeout function so it will be called after the picklist values render. Note that in your case, since you're going to the server asynchronously, you'll need to set the default value once, via setTimeout, after setting the new picklist values.

Related Topic