Improve this Doc  View Source

ngOptions

  1. - directive in module ng

The ngOptions attribute can be used to dynamically generate a list of <option> elements for the <select> element using the array or object obtained by evaluating the ngOptions comprehension expression.

In many cases, ngRepeat can be used on <option> elements instead of ngOptions to achieve a similar result. However, ngOptions provides some benefits such as reducing memory and increasing speed by not creating a new scope for each repeated instance, as well as providing more flexibility in how the <select>'s model is assigned via the select as part of the comprehension expression. ngOptions should be used when the <select> model needs to be bound to a non-string value. This is because an option element can only be bound to string values at present.

When an item in the <select> menu is selected, the array element or object property represented by the selected option will be bound to the model identified by the ngModel directive.

Optionally, a single hard-coded <option> element, with the value set to an empty string, can be nested into the <select> element. This element will then represent the null or "not selected" option. See example below for demonstration.

Complex Models (objects or collections)

By default, ngModel watches the model by reference, not value. This is important to know when binding the select to a model that is an object or a collection.

One issue occurs if you want to preselect an option. For example, if you set the model to an object that is equal to an object in your collection, ngOptions won't be able to set the selection, because the objects are not identical. So by default, you should always reference the item in your collection for preselections, e.g.: $scope.selected = $scope.collection[3].

Another solution is to use a track by clause, because then ngOptions will track the identity of the item not by reference, but by the result of the track by expression. For example, if your collection items have an id property, you would track by item.id.

A different issue with objects or collections is that ngModel won't detect if an object property or a collection item changes. For that reason, ngOptions additionally watches the model using $watchCollection, when the expression contains a track by clause or the the select has the multiple attribute. This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection has not changed identity, but only a property on the object or an item in the collection changes.

Note that $watchCollection does a shallow comparison of the properties of the object (or the items in the collection if the model is an array). This means that changing a property deeper than the first level inside the object/collection will not trigger a re-rendering.

select as

Using select as will bind the result of the select expression to the model, but the value of the <select> and <option> html elements will be either the index (for array data sources) or property name (for object data sources) of the value within the collection. If a track by expression is used, the result of that expression will be set as the value of the option and select elements.

select as and track by

Be careful when using select as and track by in the same expression.

Given this array of items on the $scope:

$scope.items = [{
  id: 1,
  label: 'aLabel',
  subItem: { name: 'aSubItem' }
}, {
  id: 2,
  label: 'bLabel',
  subItem: { name: 'bSubItem' }
}];

This will work:

<select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
$scope.selected = $scope.items[0];

but this will not work:

<select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
$scope.selected = $scope.items[0].subItem;

In both examples, the track by expression is applied successfully to each item in the items array. Because the selected option has been set programmatically in the controller, the track by expression is also applied to the ngModel value. In the first example, the ngModel value is items[0] and the track by expression evaluates to items[0].id with no issue. In the second example, the ngModel value is items[0].subItem and the track by expression evaluates to items[0].subItem.id (which is undefined). As a result, the model value is not matched against any <option> and the <select> appears as having no selected value.

Directive Info

Usage

Arguments

Param Type Details
ngModel string

Assignable angular expression to data-bind to.

name
(optional)
string

Property name of the form under which the control is published.

required
(optional)
string

The control is considered valid only if value is entered.

ngRequired
(optional)
string

Adds required attribute and required validation constraint to the element when the ngRequired expression evaluates to true. Use ngRequired instead of required when you want to data-bind to the required attribute.

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
    • label disable when disable for value in array
    • label group by group for value in array track by trackexpr
    • label disable when disable for value in array track by trackexpr
    • label for value in array | orderBy:orderexpr track by trackexpr (for including a filter with track by)
  • 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
    • label disable when disable for (key, value) in object
    • select as label group by group for (key, value) in object
    • select as label disable when disable for (key, value) in object

Where:

  • array / object: an expression which evaluates to an array / object to iterate over.
  • value: local variable which will refer to each item in the array or each property value of object during iteration.
  • key: local variable which will refer to a property name in object during iteration.
  • label: The result of this expression will be the label for <option> element. The expression will most likely refer to the value variable (e.g. value.propertyName).
  • select: The result of this expression will be bound to the model of the parent <select> element. If not specified, select expression will default to value.
  • group: The result of this expression will be used to group options using the <optgroup> DOM element.
  • disable: The result of this expression will be used to disable the rendered <option> element. Return true to disable.
  • trackexpr: Used when working with an array of objects. The result of this expression will be used to identify the objects in the array. The trackexpr will most likely refer to the value variable (e.g. value.propertyName). With this the selection is preserved even when the options are recreated (e.g. reloaded from the server).

Example

  Edit in Plunker
<script>
angular.module('selectExample', [])
  .controller('ExampleController', ['$scope', function($scope) {
    $scope.colors = [
      {name:'black', shade:'dark'},
      {name:'white', shade:'light', notAnOption: true},
      {name:'red', shade:'dark'},
      {name:'blue', shade:'dark', notAnOption: true},
      {name:'yellow', shade:'light', notAnOption: false}
    ];
    $scope.myColor = $scope.colors[2]; // red
  }]);
</script>
<div ng-controller="ExampleController">
  <ul>
    <li ng-repeat="color in colors">
      <label>Name: <input ng-model="color.name"></label>
      <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
      <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
    </li>
    <li>
      <button ng-click="colors.push({})">add</button>
    </li>
  </ul>
  <hr/>
  <label>Color (null not allowed):
    <select ng-model="myColor" ng-options="color.name for color in colors"></select>
  </label><br/>
  <label>Color (null allowed):
  <span  class="nullable">
    <select ng-model="myColor" ng-options="color.name for color in colors">
      <option value="">-- choose color --</option>
    </select>
  </span></label><br/>

  <label>Color grouped by shade:
    <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
    </select>
  </label><br/>

  <label>Color grouped by shade, with some disabled:
    <select ng-model="myColor"
          ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
    </select>
  </label><br/>



  Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
  <br/>
  <hr/>
  Currently selected: {{ {selected_color:myColor} }}
  <div style="border:solid 1px black; height:20px"
       ng-style="{'background-color':myColor.name}">
  </div>
</div>
it('should check ng-options', function() {
  expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
  element.all(by.model('myColor')).first().click();
  element.all(by.css('select[ng-model="myColor"] option')).first().click();
  expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
  element(by.css('.nullable select[ng-model="myColor"]')).click();
  element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
  expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
});