Working with select using Angular's ng-options

I have read about it in other posts but couldn't figure out. I have array:

$scope.items = [
   {ID: '000001', Title: 'Chicago'},
   {ID: '000002', Title: 'New York'},
   {ID: '000003', Title: 'Washington'},
];

I want to render this

<select>
  <option value="000001">Chicago</option>
  <option value="000002">New York</option>
  <option value="000003">Washington</option>
</select>

And also I want to select option with ID=000002.

I have read this http://docs.angularjs.org/api/ng.directive:select and tried but can't figure out.

One thing to note is that ngModel is required for ngOptions to work... note the ng-model="blah" which is saying "set $scope.blah to the selected value".

Try this:

<select ng-model="blah" ng-options="item.ID as item.Title for item in items"></select>

Here's more from Angular's documentation (if you haven't seen it):

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

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

EDIT: For some clarification on option tag values in Angular

When you use ng-options, the values of option tags written out by ng-options will always be the index of the array item the option tag relates to. This is because Angular actually allows you to select entire objects with select controls, and not just primitive types. For example:

app.controller('MainCtrl', function($scope) {
   $scope.items = [
     { id: 1, name: 'foo' },
     { id: 2, name: 'bar' },
     { id: 3, name: 'blah' }
   ];
});
<div ng-controller="MainCtrl">
   <select ng-model="selectedItem" ng-options="item as item.name for item in items"></select>
   <pre>{{selectedItem | json}}</pre>
</div>

The above will allow you to select an entire object into $scope.selectedItem directly. The point is, with Angular, you don't need to worry about what's in your option tag. Let Angular handle that, you should only care about what's in your model in your scope.

Here is a plunker demonstrating the behavior above, and showing the html written out


EDIT 2: Dealing with the default option:

There are a few things I've failed to mention above relating to the default option.

Selecting first option and removing the empty option:

You can do this by adding a simple ng-init that sets the model (from ng-model) to the first element in the items your repeating in ng-options:

<select ng-init="foo = foo || items[0]" ng-model="foo" ng-options="item as item.name for item in items"></select>

Note: this could get a little crazy if foo happens to be initialized properly to something "falsy". In that case, you'll want to handle the initialization of foo in your controller, most likely.

Customizing the default option:

This is a little different, here all you need to do is add an option tag as a child of your select, with an empty value attribute, then customize it's inner text:

<select ng-model="foo" ng-options="item as item.name for item in items">
   <option value="">Nothing selected</option>
</select>

Note: that in this case the "empty" option will stay there even after you select a different option. This isn't the case for the default behavior of selects under Angular.

A customized default option that hides after a selection is made:

If you wanted your customized default option to go away after you select a value, you can add an ng-hide attribute to your default option:

<select ng-model="foo" ng-options="item as item.name for item in items">
   <option value="" ng-if="foo">Select something to remove me.</option>
</select>

I'm learning angularjs and was struggling with selection as well. I know this question is already answered but wanted to share some more code nevertheless.

In my test I have two listboxes: car makes and car models. Models list is disabled until some make is selected. If selection in makes listbox is later reset (set to 'Select Make') then models listbox becomes disabled again AND its selection is reset as well (to 'Select Model'). Makes are retrieved as resource while models are just hard-coded.

Makes JSON:

[
{"code": "0", "name": "Select Make"},
{"code": "1", "name": "Acura"},
{"code": "2", "name": "Audi"}
]

services.js:

angular.module('makeServices', ['ngResource']).
factory('Make', function($resource){
    return $resource('makes.json', {}, {
        query: {method:'GET', isArray:true}
    });
});

HTML file:

<div ng:controller="MakeModelCtrl">
  <div>Make</div>
  <select id="makeListBox" 
      ng-model="make.selected" 
      ng-options="make.code as make.name for make in makes"
      ng-change="makeChanged(make.selected)">
  </select>

  <div>Model</div>
  <select id="modelListBox"
     ng-disabled="makeNotSelected"
     ng-model="model.selected"
     ng-options="model.code as model.name for model in models">
  </select>
</div>

controllers.js:

function MakeModelCtrl($scope)
{
    $scope.makeNotSelected = true;
    $scope.make = {selected: "0"};
    $scope.makes = Make.query({}, function (makes) {
         $scope.make = {selected: makes[0].code};
    });

    $scope.makeChanged = function(selectedMakeCode) {
        $scope.makeNotSelected = !selectedMakeCode;
        if ($scope.makeNotSelected)
        {
            $scope.model = {selected: "0"};
        }
    };

    $scope.models = [ 
      {code:"0", name:"Select Model"}, 
      {code:"1", name:"Model1"}, 
      {code:"2", name:"Model2"} 
    ];
    $scope.model = {selected: "0"};
}

Hope it makes sense and will help to somebody.

For some reason Angular allows get's me confused. Their docs are pretty horrible on this. More good examples of variations would be welcome.

Anyway, I have a slight variation on Ben Lesh's answer.

My data collections looks like this:

items =
[
   { key:"AD",value:"Andorra" }
,  { key:"AI",value:"Anguilla" }
,  { key:"AO",value:"Angola" }
 ...etc..
]

Now this :

<select ng-model="countries" ng-options="item.key as item.value for item in items"></select>

still resulted in the options value to be the index (0,1,2...etc).

Adding Track By fixed it for me:

<select ng-model="blah" ng-options="item.value for item in items track by item.key"></select>

I reckon it happens more often that you want to add a array of objects into an select list, so I am going to remember this one!

Thanks all for assisting.

In Coffeescript:

#directive
app.directive('select2',->
        templateUrl: 'partials/select.html'
        restrict: 'E'
        transclude: 1
        replace: 1
        scope:
            options: '='
            model: '='
        link: (scope, el, atr)->
            el.bind 'change', ->
                console.log this.value
                scope.model = parseInt(this.value)
                console.log scope
                scope.$apply()
    )
<!--html partial-->
<select>
<option ng-repeat='o in options'
    value='{{$index}}' ng-bind='o'></option>
</select>
<!--html usage-->
<select2 options='mnuOffline' model='offlinePage.toggle' ></select2>
<!--conclusion-->
<p>Sometimes its much easier to create your own directive..</p>

I highly recommend using Select2 (http://ivaynberg.github.io/select2/#basics) because it will handle this for you. There is even a directive for angular (https://github.com/angular-ui/ui-select2).

Altually there is a pure angular solution developed by QuantumUI.

You can find more examples and documentations here

http://angularui.net/appdoc/documents/quantumui/select/