Using $index with the AngularJS 'ng-options' directive?

AngularjsHtml Select

Angularjs Problem Overview


Say that I bind an array to a select tag using the following:

<select ng-model="selData" ng-options="$index as d.name for d in data">

In this case, the associated option tags are assigned a sequence of index values: (0, 1, 2, ...). However, when I select something from the drop-down, the value of selData is getting bound to undefined. Should the binding actually work?

On the other hand, say that I instead do the following:

<select ng-model="selData" ng-options="d as d.name for d in data">

Here, the option tags get the same index, but the entire object is bound on change. Is it working this way by design, or this behavior simply a nice bug or side-effect of AngularJS?

Angularjs Solutions


Solution 1 - Angularjs

Since arrays are very similar to objects in JavaScript, you can use the syntax for "object data sources". The trick is in the brackets in the ng-options part:

var choices = [
  'One',
  'Two',
  'Three'
];

In the template:

<select
  ng-model="model.choice"
  ng-options="idx as choice for (idx, choice) in choices">
</select>

In the end, model.choice will have the value 0, 1, or 2. When it's 0, you will see One; 1 will display Two, etc. But in the model, you will see the index value only.

I adapted this information from "Mastering Web Application Development with AngularJS" by PACKT Publishing, and verified at the Angular reference documentation for select.

Solution 2 - Angularjs

Since you can't use $index but you can try indexOf.

HTML

<div ng-app ng-controller="MyCtrl">
    <select 
          ng-model="selectedItem"
          ng-options="values.indexOf(selectedItem) as selectedItem for selectedItem in values"></select>
    selectedItem: {{selectedItem}}
</div>

Controller

function MyCtrl($scope) {
    $scope.values = ["Value1","Value2"];
    $scope.selectedItem = 0;
}

Demo Fiddle

Comment:

Array.prototype.indexOf is not supported in IE7 (8)

Solution 3 - Angularjs

$index is defined for ng-repeat, not select. I think this explains the undefined. (So, no, this shouldn't work.)

Angular supports binding on the entire object. The documentation could be worded better to indicate this, but it does hint at it: "ngOptions ... should be used instead of ngRepeat when you want the select model to be bound to a non-string value."

Solution 4 - Angularjs

You can also use ng-value='$index' in <option> tag.

<select ng-model="selData">
 <option ng-repeat="d in data track by $index" ng-value="$index">
   {{d.name}}
 </option>
</select>

Solution 5 - Angularjs

Don't use $index inside select tags. Use $index inside the option tags if you want to use the array indexes as either values or options.

<option ng-repeat="user in users" value="{{user.username}}">{{$index+1}}</option>

If you want to use inside values just put it in the value attribute as binding expression like

<option ng-repeat="user in users" value="{{$index+1}}">{{user.username}}</option>

and my controller code be like:

var users = ['username':'bairavan', 'username':'testuser'];

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJim CoteView Question on Stackoverflow
Solution 1 - AngularjsHarry PehkonenView Answer on Stackoverflow
Solution 2 - AngularjsMaxim ShoustinView Answer on Stackoverflow
Solution 3 - AngularjsMark RajcokView Answer on Stackoverflow
Solution 4 - AngularjsBrian McAuliffeView Answer on Stackoverflow
Solution 5 - AngularjsBairavanView Answer on Stackoverflow