Here is what seems to be bothering a lot of people (including me).
When using the ng-options
directive in AngluarJS to fill in the options for a <select>
tag I cannot figure out how to set the value for an option. The documentation for this is really unclear - at least for a simpleton like me.
I can set the text for an option easily like so:
ng-options="select p.text for p in resultOptions"
when resultOptions
is for example:
[
{
"value": 1,
"text": "1st"
},
{
"value": 2,
"text": "2nd"
}
]
Should be (and probably is) the most simple thing to set the option values, but so far I just don't get it.
http://docs.angularjs.org/api/ng.directive:select
ngOptions(optional) – {
comprehension_expression=
} – in one of the following forms:for array data sources:
label for value in array
select as label for value in array
label group by group for value in array
select as label group by group for value in array track by trackexpr
for object data sources:
label for (key , value) in object
select as label for (key , value) in object
label group by group for (key, value) in object
select as label group by group for (key, value) in object
In your case, it should be
array = [{ "value": 1, "text": "1st" }, { "value": 2, "text": "2nd" }];
<select ng-options="obj.value as obj.text for obj in array"></select>
With the updates on Angular, it is now possible to set the actual value for the value
attribute of select
element with track by
expression.
<select ng-options="obj.text for obj in array track by obj.value">
</select>
To all the people who are having hard time to remember this syntax form: I agree this isn't the most easiest or beautiful syntax. This syntax is kind of an extended version of Python's list comprehensions and knowing that helps me to remember the syntax very easily. It's something like this:
Python code:
my_list = [x**2 for x in [1, 2, 3, 4, 5]]
> [1, 4, 9, 16, 25]
# let people to be a list of person instances
my_list2 = [person.name for person in people]
> my_list2 = ['Alice', 'Bob']
This is actually the same syntax as the first one listed above. However, in <select>
we usually need to differentiate between the actual value in code and the text shown (the label) in a <select>
element. Like, we need person.id
in the code but we don't want to show the id
to the user, we want to show its name. Likewise, we're not interested in the person.name
in the code. There comes as
keyword to label stuff. So it becomes like this:
person.id as person.name for person in people
Or, instead of person.id
we could need the person
instance/reference itself. See below:
person as person.name for person in people
For JS objects, the same method applies as well, just remember that the items in the object is deconstructed with (key, value)
pairs.
How the value attributes gets its value:
So in your case it should be:
obj = { '1': '1st', '2': '2nd' };
<select ng-options="k as v for (k,v) in obj"></select>
I had this issue too. I wasn't able to set my value in ng-options. Every option that is generate was set with 0, 1, ..., n.
To make it right, i did in my ng-options something like...
HTML:
<select ng-options="room.name for room in Rooms track by room.price">
<option value="">--Rooms--</option>
</select>
I use "track by" to set all my value with room.price
(This example sucks: cause if there were more one price equal, the code will fail. so BE SURE have diferents values)
JSON:
$scope.Rooms = [
{ name: 'SALA01', price: 100 },
{ name: 'SALA02', price: 200 },
{ name: 'SALA03', price: 300 }
];
I learn it here: http://gurustop.net/blog/2014/01/28/common-problems-and-solutions-when-using-select-elements-with-angular-js-ng-options-initial-selection/
Wacth the video, its a nice class :)
If you want to change the value of your option
elements because the form will eventually be submitted to the server, instead of doing this :
<select name="text" ng-model="text" ng-options="select p.text for p in resultOptions"></select>
You can do this :
<select ng-model="text" ng-options="select p.text for p in resultOptions"></select>
<input type="hidden" name="text" value="{{ text }}" />
The expected value will then be sent through the form under the correct name.
To send a custom value called my_hero
to the server using a normal form submit:
JSON:
"heroes": [
{"id":"iron", "label":"Iron Man Rocks!"},
{"id":"super", "label":"Superman Rocks!"}
]
HTML:
<select ng-model="hero" ng-options="obj.id as obj.label for obj in heroes"></select>
<input type="hidden" name="my_hero" value="{{hero}}" />
The server will receive either iron
or super
as the value of my_hero
.
Similar to the answer by @neemzy, but specifying separate data for the value
attribute.
It appears that ng-options
is complicated (possibly frustrating) to use but in reality what we have here is an architecture problem.
AngularJS serves as an MVC framework for a dynamic HTML+JS application. While its (V)iew component does offer HTML "templating," its primary purpose is to connect user actions, via a controller, to changes in the model. Therefore the appropriate level of abstraction, from which to work in AngularJS, is that a select element sets a value in the model to a value from a query.
ng-options
provides the for
keyword to dictate what the contents of the option element should be i.e. p.text for p in resultOptions
.ng-options
provides the as
keyword to specify what value is provided to the model as in k as v for (k,v) in objects
.The correct solution this is problem is then architectural in nature and involves refactoring your HTML so that the (M)odel performs server communication when required (instead of the user submitting a form).
If an MVC HTML page is unnecessary over-engineering for the problem at hand: then use only the HTML generation portion of AngularJS’s (V)iew component. In this case, follow the same pattern that is used for generating elements such as <li />
's under <ul />
's and place a ng-repeat on an option element:
<select name=“value”>
<option ng-repeat=“value in Model.Values” value=“{{value.value}}”>
{{value.text}}
</option>
</select>
As a kludge, one can always move the name attribute of the select element to a hidden input element:
<select ng-model=“selectedValue” ng-options=“value.text for value in Model.Values”>
</select>
<input type=“hidden” name=“value” value=“{{selectedValue}}” />
You can do this:
<select ng-model="model">
<option value="">Select</option>
<option ng-repeat="obj in array" value="{{obj.id}}">{{obj.name}}</option>
</select>
-- UPDATE
After some updates, the solution of frm.adiputra is much better: How to set value property in angularjs ng-options? Code:
obj = { '1': '1st', '2': '2nd' };
<select ng-options="k as v for (k,v) in obj"></select>
This is how I resolved this. Tracked the select by value and set the selected item property to the model in my javascript.
<select ng-model="vm.Enterprise.AdminCountry" ng-options="country.CountryName for country in vm.Countries track by country.CountryId">
Country = [{CountryId =1, Code = 'USA', CountryName='United States of America'}, {CountryId =2, Code = 'CAN', CountryName='Canada'}]
vm is my controller and the Country in the controller retrieved from the service is {CountryId =1, Code = 'USA', CountryName='United States of America'}
When I selected another country from the select dropdown and posted my page with "Save", I get the correct country bound.
Instead of using the new 'track by' feature you can simply do this with an array if you want the values to be the same as the text:
<select ng-options="v as v for (k,v) in Array/Obj"></select>
Note the difference between the standard syntax, which will make the values the keys of the Object/Array, and therefore 0,1,2 etc. for an array:
<select ng-options"k as v for (k,v) in Array/Obj"></select>
k as v becomes v as v.
I discovered this just based on common sense looking at the syntax. (k,v) is the actual statement that splits the array/object into key value pairs.
In the 'k as v' statement, k will be the value, and v will be the text option displayed to the user. I think 'track by' is messy and overkill.
<select ng-model="color" ng-options="(c.name+' '+c.shade) for c in colors"></select><br>
The ng-options
directive does not set the value attribute on the <options>
elements for arrays:
Using limit.value as limit.text for limit in limits
means:
set the
<option>
's label aslimit.text
save thelimit.value
value into the select'sng-model
I have struggled with this problem for a while today. I read through the AngularJS documentation, this and other posts and a few of blogs they lead to. They all helped me grock the finer details, but in the end this just seems to be a confusing topic. Mainly because of the many syntactical nuances of ng-options
.
In the end, for me, it came down to less is more.
Given a scope configured as follows:
//Data used to populate the dropdown list
$scope.list = [
{"FirmnessID":1,"Description":"Soft","Value":1},
{"FirmnessID":2,"Description":"Medium-Soft","Value":2},
{"FirmnessID":3,"Description":"Medium","Value":3},
{"FirmnessID":4,"Description":"Firm","Value":4},
{"FirmnessID":5,"Description":"Very Firm","Value":5}];
//A record or row of data that is to be save to our data store.
//FirmnessID is a foreign key to the list specified above.
$scope.rec = {
"id": 1,
"FirmnessID": 2
};
This is all I needed to get the desired result:
<select ng-model="rec.FirmnessID"
ng-options="g.FirmnessID as g.Description for g in list">
<option></option>
</select>
Notice I did not use track by
. Using track by
the selected item would alway return the object that matched the FirmnessID, rather than the FirmnessID itself. This now meets my criteria, which is that should return a numeric value rather than the object, and to use ng-options
to gain the performance improvement it provides by not creating a new scope for each option generated.
Also, I needed the blank first row, so I simply added an <option>
to the <select>
element.
Here is a Plunkr that shows my work.
It is always painful for developers to with ng-options. For example:Getting empty/blank selected value in the select tag. Especially when dealing with JSON object in ng-options, it becomes more tedious.Here I have done some exercises on that.
Objective: Iterate array of JSON objects through ng-option and set selected first element.
Data:
someNames = [{"id":"1","someName":"xyz"}, {"id":"2","someName":"abc"}]
In select tag I had to show xyz and abc, where xyz must be selected without much efforts.
HTML:
<pre class="default prettyprint prettyprinted" style=""><code>
<select class="form-control" name="test" style="width:160px" ng-options="name.someName for name in someNames" ng-model="testModel.test" ng-selected = "testModel.test = testModel.test || someNames[0]">
</select>
</code></pre>
By above code sample, you might get out of this exaggeration.
Another reference:
The correct answer to this question has been provided by frm.adiputra, as currently this seems to be the only way to explicitly control the value attribute of the option elements.
However, I just wanted to emphasize that "select" is not a keyword in this context, but it is just a placeholder for an expression. Please refer to the following list, for the definition of the "select" expression as well as other expressions that can be used in ng-options directive.
The use of select as it is depicted in the question:
ng-options='select p.text for p in resultOptions'
is essentially wrong.
Based on the list of expressions, it seems that trackexpr may be used to specify the value, when options are given in an array of objects, but it has been used with grouping only.
From AngularJS' documentation for ng-options:
The following tutorial help me solve the problem: ANGULAR.JS: NG-SELECT AND NG-OPTIONS
<select id="countryId"
class="form-control"
data-ng-model="entity.countryId"
ng-options="value.dataValue as value.dataText group by value.group for value in countries"></select>
For an object:
<select ng-model="mySelect" ng-options="key as value for (key, value) in object"></select>
For me the answer by Bruno Gomes is the best answer
But actually, you need not worry about setting the value property of select options, angularJS will take care of that. Let me explain in detail.
angular.module('mySettings', []).controller('appSettingsCtrl', function ($scope) {
$scope.timeFormatTemplates = [{
label: "Seconds",
value: 'ss'
}, {
label: "Minutes",
value: 'mm'
}, {
label: "Hours",
value: 'hh'
}];
$scope.inactivity_settings = {
status: false,
inactive_time: 60 * 5 * 3, // 15 min (default value) ie 900 seconds
//time_format: 'ss', // second (default value)
time_format: $scope.timeFormatTemplates[0], // default seconds object
};
$scope.activity_settings = {
status: false,
active_time: 60 * 5 * 3, // 15 min (default value) ie 900 seconds
//time_format: 'ss', // second (default value)
time_format: $scope.timeFormatTemplates[0], // default seconds object
};
$scope.changedTimeFormat = function (time_format) {
'use strict';
console.log('time changed');
console.log(time_format);
var newValue = time_format.value;
// do your update settings stuffs
}
});
As you can see in fiddle output, what ever you choose for select box options, it your custom value, or the 0, 1, 2 auto generated value by angularjs, it doesnot matter in your output unless you are using jquery or any other library to access value of that select combo box options and manipulate it accordingly.
Selecting an item in ng-options can be a bit tricky depending on how you set the data source.
After struggling with them for a while I end up making a sample with most common data sources I use, you can find it here:
http://plnkr.co/edit/fGq2PM?p=preview
Now to make ng-options work here are some things to consider:
key | label
, many online examples put objects as 'key'. If you need information from the object set it that way, otherwise use the specific property you need as key. (ID, CODE, etc.. As in the plckr sample)To way to set the value of the dropdown/select control depends on #3,
$scope.dropdownmodel = $scope.user.state;
If you set the object as key, you need to loop trough the options, even assigning the object will not set the item as selected as they will have different hashkeys, e.g.:
for (var i = 0, len = $scope.options.length; i < len; i++) {
if ($scope.options[i].id == savedValue) { // Your own property here:
console.log('Found target! ');
$scope.value = $scope.options[i];
break;
}
}
You can replace savedValue for the same property in the other object $scope.myObject.myProperty
A year after the question, I had to find an answer for this question as non of these gave the actual answer, at least to me.
You have asked how to select the option, but nobody has said that this two things are NOT the same:
If we have an options like this:
$scope.options = [
{ label: 'one', value: 1 },
{ label: 'two', value: 2 }
];
And we try to set a default option like this:
$scope.incorrectlySelected = { label: 'two', value: 2 };
It will NOT work, but if you try to select the option like this:
$scope.correctlySelected = $scope.options[1];
It will WORK
Even though these two objects have the same properties, Angular is considering them as a DIFFERENT because Angular compares by the reference.
Take a look at this fiddle http://jsfiddle.net/qWzTb/
Here is how I solve this problem in a legacy app:
In HTML:
ng-options="kitType.name for kitType in vm.kitTypes track by kitType.id" ng-model="vm.itemTypeId"
In script:
vm.kitTypes = [
{"id": "1", "name": "Virtual"},
{"id": "2", "name": "Physical"},
{"id": "3", "name": "Hybrid"}
];
...
vm.itemTypeId = vm.kitTypes.filter(function(value, index, array){
return value.id === (vm.itemTypeId || 1);
})[0];
My HTML displays the option value properly
<select ng-model="output">
<option ng-repeat="(key,val) in dictionary" value="{{key}}">{{val}}</option>
</select>
Like many said it before, if i have data something like this:
countries : [
{
"key": 1,
"name": "UAE"
},
{
"key": 2,
"name": "India"
},
{
"key": 3,
"name": "OMAN"
}
]
I would use it like
<select
ng-model="selectedCountry"
ng-options="obj.name for obj in countries">
</select>
in your Controller you need to set initial value to get rid first empty item
$scope.selectedCountry = $scope.countries[0];
// you need to watch changes to get selected value
$scope.$watchCollection(function () {
return $scope.selectedCountry
}, function (newVal, oldVal) {
if (newVal === oldVal) {
console.log("nothing has changed "+$scope.selectedCountry)
}
else {
console.log('new value '+$scope.selectedCountry)
}
}, true)
This suites best for all scenario according to me.
<select ng-model="mySelection.value">
<option ng-repeat="r in myList" value="{{r.Id}}" ng-selected="mySelection.value == r.Id">{{r.Name}}
</option>
</select>
where you can use you model to bind the data, you will get the value as the object will contain and the default selection based on your scenario.