568

Here is what seems to be bothering a lot of people (including me).

When using the ng-options directive in AngularJS 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"
    }
]

It should be (and probably is) the most simple thing to set the option values, but so far I just don't get it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jukka Puranen
  • 8,026
  • 6
  • 27
  • 25
  • 12
    Had the same problem because I didn't find the docs (as shown below) very clear. – benvds Nov 13 '12 at 16:32
  • 1
    The use of "select" as it is depicted in the question is essentially wrong, because "select" in this context, is not a keyword, but it is a place holder for an expression. This is from AngularJS' documentation: "select: The result of this expression will be bound to the model of the parent element. If not specified, select expression will default to value." I have provide more detail in my answer below. – Majix May 05 '14 at 15:18
  • For me this is the best answer. http://stackoverflow.com/questions/12139152/how-to-set-value-property-in-angularjs-ng-options#22997081 – Sanjay Mar 12 '15 at 09:10
  • 1
    Note: For ng-options to work, ng-model is mandatory!!!!!!!!! Ref:http://stackoverflow.com/a/13049740/234110 – Anand Rockzz Apr 05 '15 at 14:17
  • http://stackoverflow.com/questions/18615624/angularjs-ng-options-with-group/37035297#37035297 – Shafqat May 04 '16 at 18:31
  • When using ng-options first make sure the array is setup correctly. Secondly the text value is set first, at the end the value/Id is set by track by x.x x.ChannelName for x in DistChannelList[0] track by x.DistributionChannelID – Deathstalker Feb 05 '20 at 19:14

27 Answers27

712

See ngOptions

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>

Update

With the updates on AngularJS, 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>

How to remember this ugly stuff

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 the 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 JavaScript objects, the same method applies as well. Just remember that the items in the object is deconstructed with (key, value) pairs.

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Umur Kontacı
  • 35,403
  • 8
  • 73
  • 96
  • 33
    Your example doesn't fill value attribute of option. It defines what would be stored in the model of – Artem Andreev Aug 27 '12 at 10:53
  • It does fill the attribute of option with the `obj.value`. Just like in your example. – Umur Kontacı Aug 27 '12 at 10:57
  • 57
    Strange but I get , in Chrome and FF. – Artem Andreev Aug 27 '12 at 11:04
  • 1
    Ok, I get it now. I was expecting to see select tag with where the options values come from obj.value (which they don't) but for me its enough that the model has the selected object. Many thanks for the help. – Jukka Puranen Aug 27 '12 at 11:11
  • 1
    Sorry, my bad, I was looking at the bound model, it does not set the value. But you don't need it if you don't need to access it outside of angular. – Umur Kontacı Aug 27 '12 at 11:13
  • I think there's a slight bug here. If you put any other number in for values, it will never actually generate those values into html values. For example, if you write "anything in a string" as a value, it'll still produce incrementing numbers for your values. – Trip Jan 30 '13 at 22:11
  • 49
    It's not bug, it's a feature. angularjs handles ngOptions and ngModel internally, this way it allows you to use any type of object as value in option rather than only strings. You should never try to get the value of the select *the other way around* you just need to use ngModel you attached to select element. – Umur Kontacı Jan 31 '13 at 06:48
  • 1
    The `select` needs an `ng-model` attribute for it to do anything. This is a weird issue I encountered today in Angular that I intend to report as a bug; select will not show options from `ng-options` without also having an `ng-model` attribute. – Ezekiel Victor Jul 24 '13 at 07:51
  • Well, without `ng-model`, `select` and `ng-options` are pretty useless. Also, it would make ambigious which option is selected, since there is no `ng-model`. – Umur Kontacı Jul 24 '13 at 10:15
  • 4
    I beg to differ. Internally the select directive could easily use its own invisible model to track which option is selected. Even if it didn't track the model value internally, it could at least render the options and just prepend and select the empty option (which is the behavior that it does now if you set the model to a value not in the option set). The only dependency for showing options is the options themselves -- POLA principle applies here. – Ezekiel Victor Aug 18 '13 at 11:04
  • Also, it is not necessarily useless. I can posit plenty of use cases such that `select` is used with a custom directive that supplants the need for `ngModel`. The transparent requirement of `ngModel` is counter intuitive. (On that note, at the very least an informative exception is in order.) – Ezekiel Victor Aug 18 '13 at 11:06
  • I believe it would be easier and conside to develop a select directive that just syncs over `ngModel` anything else kinda violates DRY. For creating a directive supplants `ngModel`, I would go along with creating a directive that works together with `ngModel`. However, I definitely agree on silent failing issue, though. – Umur Kontacı Aug 18 '13 at 11:46
  • 3
    Why does this answer have so many up votes when it does not provide the answer? frm.adiputra's answer is correct. – Noishe Dec 25 '13 at 09:59
  • @Noishe - his answer uses key-value pairs, not object property names. – Ben May 15 '14 at 19:23
  • @edthethird Strange because 1.2.15 docs still offer the same documentation: https://code.angularjs.org/1.2.15/docs/api/ng/directive/select – Umur Kontacı Jun 04 '14 at 09:32
  • Not working for me as of v1.2.21. The value is ignored. The object syntax works, but then of course the option order is undefined. – numbers1311407 Aug 04 '14 at 12:21
  • 1
    The answer from @BrunoGomes using `track by` works for v1.2.21. – numbers1311407 Aug 04 '14 at 12:27
  • Every time when I want do some manipulations with ng-options I appear on stackoverflow. Because syntax and documentation so UGLY for solid framework like this. – alexche8 Nov 20 '14 at 15:16
  • If anyone on the AngularJS team discovers this, please update the documentation. It is horribly confusing and I wasted quite a long time before discovering this answer. – Yaron Feb 11 '15 at 02:16
  • Excellent answer - thank you. The update with the track by syntax was particularly useful. Far better than the angular documentation - it made their documentation useful. – bmacnaughton Apr 29 '15 at 15:48
  • This works for me to display, but I'm having trouble when setting the value of the model in my controller. Should i be setting it as the ID or the value? Neither seems to work. – zekone Jun 10 '16 at 12:55
  • Thanks for pointing out the similarity of this syntax with Python! Excellent explanation. – themefield Nov 11 '21 at 00:23
138

How the value attributes gets its value:

  • When using an array as datasource, it will be the index of the array element in each iteration;
  • When using an object as datasource, it will be the property name in each iteration.

So in your case it should be:

obj = { '1': '1st', '2': '2nd' };

<select ng-options="k as v for (k,v) in obj"></select>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
frm.adiputra
  • 1,497
  • 1
  • 8
  • 3
  • 16
    +1 for explaining how Angular sets the value attribute of the option elements. – Mark Rajcok Dec 11 '12 at 16:49
  • 2
    This does not set a value. Values default to numbers. But you can't actually specify a "value".. rediculous.. – Trip Jan 30 '13 at 22:21
  • 13
    @Trip, if you 'inspect' the select options, you will see numbers, but if you look at the bound value in the model, the value is in this case the 'k' value. It's confusing. blesh has a plunk that shows this in this answer: http://stackoverflow.com/questions/13047923/working-with-ng-options-in-angular http://plnkr.co/edit/vJOljg?p=preview – TsenYing Feb 20 '13 at 18:43
  • Take note that the model of the select tag should be a different model from the source of the options. – raberana Nov 23 '13 at 12:40
  • 4
    This is should be included into the angular document, short and precise. +1 – devric Apr 28 '14 at 10:02
  • 2
    This is the only solution that worked for me. What a nasty headache. – Jon Jun 12 '14 at 01:35
  • Thank you, dude. You saved my life today. This worked. Default one is: dreadful ugly syntax from Angular. – webblover Jun 18 '14 at 10:16
  • This does not really work, because the order of the keys is undefined – jameshfisher Jun 25 '14 at 16:50
  • @jameshfisher You can add `| orderBy: 'toString()'` to sort by the name. – nilskp Sep 07 '14 at 22:22
  • @frm.adiputra Your example doesn't seem to work, here's a fiddle: http://jsfiddle.net/zFtkR/107/ – adamj Dec 27 '15 at 16:32
123

I had this issue too. I wasn't able to set my value in ng-options. Every option that was generated was set with 0, 1, ..., n.

To make it right, I did something like this in my ng-options:

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 values with room.price.

(This example sucks: because if there were more than one price equal, the code would fail. So BE SURE to have different values.)

JSON:

$scope.Rooms = [
            { name: 'SALA01', price: 100 },
            { name: 'SALA02', price: 200 },
            { name: 'SALA03', price: 300 }
        ];

I learned it from blog post How to set the initial selected value of a select element using Angular.JS ng-options & track by.

Watch the video. It's a nice class :)

Community
  • 1
  • 1
Bruno Gomes
  • 1,435
  • 1
  • 11
  • 14
  • 8
    This works perfectly. On top of it, it allows to use a datalist element with a contained – climboid May 07 '14 at 15:57
  • 9
    This is the **most appropriate answer for** the ng-options with option box's value to match the array/object. I would have given **10000 points** for you for saving everyone's day if there is a rewards exchange system. Most bizarre syntax from angular team. – webblover Jul 11 '14 at 06:36
  • 2
    THANK YOU! This was so frustrating for a long time! – Sean Thompson Jul 30 '14 at 22:39
  • 2
    I hope the room prices are never the same, because then this breaks. – nilskp Sep 07 '14 at 22:24
  • Is it possible to make an ajax call for ng-options as soon as select is clicked? – streetlight Nov 24 '14 at 20:15
  • I think so... but you will click, call the request... and the select in this moment wont be filled, you should click again in the select... but will call the request again @_@ and again and again – Bruno Gomes Nov 25 '14 at 12:44
  • 1
    the track by is the key! – sidonaldson Mar 11 '15 at 12:44
  • 2
    This should be by far the best answer and selected for the answer, since this is the only solution that worked for me. Actually, in AngularJS, you need not worry about what comes in value, ie 0,1,2 (which comes by default), because you can get the whole lot of object as value. Please refer to this answer http://stackoverflow.com/questions/12139152/how-to-set-value-property-in-angularjs-ng-options#29005692 – Sanjay Mar 12 '15 at 09:14
  • 2
    This is a better answer to this particular question but everyone keeps up voting the copy/paste documentation – Erik Grosskurth May 17 '16 at 14:01
  • 1
    Awesome summary, Bruno :) – Meligy May 21 '16 at 06:24
  • @BrunoGomes I need help Please view ones this [Question](http://stackoverflow.com/q/41223958/2143446) – Neotrixs Dec 20 '16 at 07:33
47

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
neemzy
  • 1,899
  • 23
  • 25
  • 2
    I would add that, rather than use ``, I'd use `ng-value`. So: ``. – Dan Atkinson Apr 03 '14 at 14:41
  • to clarify in my case, i am not submitting the form through Angular using a json post--rather i am submitting the form normally using http post, so overriding the values that Angular puts in the tag allows me to submit the correct value (thanks!). I did not need to use ng-value in the hidden input, rather i needed to set the value tag as the original post notes. – FireDragon May 27 '14 at 17:46
26

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.

This is similar to the answer by @neemzy, but specifying separate data for the value attribute.

Community
  • 1
  • 1
Philip Bulley
  • 9,014
  • 3
  • 33
  • 46
24

It appears that ng-options is complicated (possibly frustrating) to use, but in reality we have an architecture problem.

AngularJS serves as an MVC framework for a dynamic HTML+JavaScript 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.

  • How a query row is presented to the user is the (V)iew’s concern and 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.
  • How a selected row is presented to the server is the (M)odel’s concern. Therefore 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 &lt;li /&gt;'s under &lt;ul /&gt;'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 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}}” />
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Joshcodes
  • 8,513
  • 5
  • 40
  • 47
  • bad performance when using ng-repeat on an option element, possibly? you are supposed to use ng-options on the select itself. – DrCord Apr 20 '14 at 16:49
  • @DrCord: Using ng-repeat on the option element is offered as a solution only in isolated cases (when a proper MVC HTML pages is unnecessary over-engineering). Maybe the downvoter didn't ready it very closely. It will be edited to call out more specifically that ng-repeat on option element is not the ideal case. – Joshcodes Apr 20 '14 at 17:29
  • 1
    As an Angular N00b I would have selected this solution because in the first instance it looks like a fair suggestion and likely to work. The syntax suggested in other answers is, to say the least, bizarre - even if one of the many choices happens to work. It is a legitimate solution and if it avoids premature optimisation then it's fine. Down voting is just mean it seems. – Ian Lewis Sep 04 '14 at 16:27
  • 2
    This is what it says on the AngularJS website: https://docs.angularjs.org/api/ng/directive/select. ```"Note: ngOptions provides an iterator facility for the – Ian Lewis Sep 05 '14 at 10:09
  • @IanLewis, thanks, that makes sense. It did not make any sense to me why ngRepeat would be any less performant on an unbound select's options than it is on an
  • tag.
  • – Joshcodes Sep 05 '14 at 14:27