+ Start a Discussion
Bobby KnezevicBobby Knezevic 

<ui:inputSelectOption> label is "undefined" when retrieving using selectCmp.get("v.label")

I have a very simple <ui:inputSelectOption> element and a button that calls a controller action that examines the element's label and value. However, the label is "undefined" on the select element.
 
<ui:inputSelect aura:id="pricebooks">
    <ui:inputSelectOption text="" label="--None--" />
    <aura:iteration items="{!v.pricebooks}" var="pricebook">
        <ui:inputSelectOption text="{!pricebook.Id}" label="{!pricebook.Name}" />
    </aura:iteration>
</ui:inputSelect>

<button onclick="{!c.selectPricebook}">Select</button>

Controller code:
({
    selectPricebook: function(component, event, helper) {
        var selectCmp = component.find("pricebooks");
	var value = selectCmp.get("v.value");
        console.log(value); // The expected result
        var label = selectCmp.get("v.label");
        console.log(label); // undefined
})

I've examined the selectCmp object in the debugger and the label property is, in fact, "undefined". What could be the problem here?
Best Answer chosen by Bobby Knezevic
Bobby KnezevicBobby Knezevic
This appears to be a core omission from the Aura framework. I was able to locate their docs and none of their examples for the inputSelect component retrieve the label of the selected option. The majority of their examples simple set the "text" attribute which the framework appears to use as the label by default.

In my experience, I've used a select option as a means to store key-value pairs without having to populate a controller-side data structure. However, in this case I was forced to. My solution is a quick and dirty one.

Solution:
// Controller
({
    doInit: function(component, event, helper) {
        helper.getPricebooks(component);
    },
    selectPricebook: function(component, event, helper) {
        var selectCmp = component.find("pricebooks");
        var value = selectCmp.get("v.value");
        var label = helper.getPricebookName(value);
        console.log(label); // Expected value
    }
})


// Helper
({
    pricebookIdToNameMap: {},
    getPricebooks: function(component) {
        var action = component.get("c.getPricebooks");

        var self = this;

        function buildPricebookMap(pricebooks) {
            for (var index in pricebooks) {
                self.pricebookIdToNameMap[pricebooks[index].Id] = pricebooks[index].Name;
            }
        }

        action.setCallback(this, function(actionResult) {
            var pricebooks = actionResult.getReturnValue();

            buildPricebookMap(pricebooks);
            component.set("v.pricebooks", pricebooks);
        });

        $A.enqueueAction(action);
    },
    getPricebookName: function(id) {
        return this.pricebookIdToNameMap[id];
    }
})

 

All Answers

Bobby KnezevicBobby Knezevic
Actually, when I'm getting the following message on screen: Something has gone wrong. label is not defined. Please try again.
Bobby KnezevicBobby Knezevic
This appears to be a core omission from the Aura framework. I was able to locate their docs and none of their examples for the inputSelect component retrieve the label of the selected option. The majority of their examples simple set the "text" attribute which the framework appears to use as the label by default.

In my experience, I've used a select option as a means to store key-value pairs without having to populate a controller-side data structure. However, in this case I was forced to. My solution is a quick and dirty one.

Solution:
// Controller
({
    doInit: function(component, event, helper) {
        helper.getPricebooks(component);
    },
    selectPricebook: function(component, event, helper) {
        var selectCmp = component.find("pricebooks");
        var value = selectCmp.get("v.value");
        var label = helper.getPricebookName(value);
        console.log(label); // Expected value
    }
})


// Helper
({
    pricebookIdToNameMap: {},
    getPricebooks: function(component) {
        var action = component.get("c.getPricebooks");

        var self = this;

        function buildPricebookMap(pricebooks) {
            for (var index in pricebooks) {
                self.pricebookIdToNameMap[pricebooks[index].Id] = pricebooks[index].Name;
            }
        }

        action.setCallback(this, function(actionResult) {
            var pricebooks = actionResult.getReturnValue();

            buildPricebookMap(pricebooks);
            component.set("v.pricebooks", pricebooks);
        });

        $A.enqueueAction(action);
    },
    getPricebookName: function(id) {
        return this.pricebookIdToNameMap[id];
    }
})

 
This was selected as the best answer
Jorge Esparza 4Jorge Esparza 4

I just ran into this same issue today, and I just wanted to share my solution in case someone else gets stuck with this. I will be using Bobby's code to reproduce my answer.

I found it a little less code expensive to create a new component to hold the inputSelect, and on my new component have an event fire when the inputSelecOption is clicked. That way you are guaranteed access to the label and text values from the inputSelectOption.

<aura:registerevent name="AnEventComponent" type="c:CustomEvent" />
<ui:inputSelect aura:id="pricebooks">
    <ui:inputSelectOption text="" label="--None--" />
    <aura:iteration items="{!v.pricebooks}" var="pricebook">
        <ui:inputSelectOption text="{!pricebook.Id}" label="{!pricebook.Name}" click='{!c.readValues}'/>
    </aura:iteration>
</ui:inputSelect>

On the new component's controller I just grab the values I need, wrap them on an event, and fire it.
// Controller
({
    readValues: function(component, event, helper) {
        var appEvent = component.getEvent("AnEventComponent");
		var pricebookChoice = event.getSource();
		
		appEvent.setParams({
			"PricebookId" : pricebookChoice.get(v.text),
			"PricebookName" : pricebookChoice.get(v.label)
		});
		
		appEvent.fire();
    }
})

Leaving the initial code looking something like:
<aura:handler name="eventHandled" event="c:AnEventComponent" action="{!c.selectPricebook}" />
<c:priceBookSelect>
({
    selectPricebook: function(component, event, helper) {
		var value = event.getParam("PricebookId");
        console.log(value); // Expected result
        var label = event.getParam("PricebookName");
        console.log(label); // Expected result
	}
})

This will avoid another apex call, as well as avoiding the for loop on the Javascript controller.

This also removes the submit button, which might be an issue depending on what you want to do with the inputSelectOption information. We wouldn't want to create an object every time, someone clicks an option. But then it can just be a matter of creating an object in memory, firing it as an event, and then let the submit button deal with the event on time.
 
In the end the rule of thumb for this scenarios (a rule taught to me by my lead dev), is that: "If there's an issue with information sharing, it maybe that the component you are trying to use should not have that knowledge/responsibility. Delegate to another component and let it handle that responsibility/knowledge."