Last Updated: September 29, 2021
·
67.22K
· theatlasroom

ng-repeat $index for a form field array (ie add new option)

I've been blindly fighting my way through angularjs, while its been enjoyable for the most part there have been a few things here and there that haven't been immediately obvious (to me).

One of these cases was setting up a form field that could be duplicated and result in an array with values bound to each seperate field.

For example:

Picture

The campus field is 1 field in the model, but it can have an array of values sent to the server ie

model.campuses = []
model.campuses = ['campus 1', 'campus 2']

The simplest solution I could find for this was to use an ng-repeat container element, and make use of the $index property that indicates the current item in the ng-repeated array. Then the 'Add another campus' button simply binds to a function that pushes an empty array onto the campuses object.

Here's some code to illustrate this. First off, here is the js code in the controller:

model.campuses = [{}]; // initialise the array with an empty object
function addCampus(){ 
  // push an empty object onto the array
  $scope.model.campuses.push({});
}

And here is the HTML for the form fields

<div ng-repeat="item in model.campuses" class="form-group row">
  <label>Campus</label> 
  <select class="form-control" ng-model="model.campuses[$index]">
    <option>Select a campus</option>
  </select>
</div>
<button ng-click="addCampus()">Add another campus</button>

5 Responses
Add your response

Referencing item from ng-repeat in ng-model should still work.

<select class="form-control" ng-model="item">
    <option>Select a campus</option>
 </select>
over 1 year ago ·

@creatordave Well that's what I thought, but it doesnt seem to work, here are some jsfiddles.

over 1 year ago ·

Hi,

you shouldn't bind directly to the key object in ng-repeat, because this object is not the original object from your array. That's a caveat when you are binding to a primitive array.

ng-repeat creates a new scope for each iteration. Given we have an expression like

ng-repeat="item in items" 

where items is defined as ["a", "b", "c"], AngularJS projects these values into new objects, so you basically have objects like { item: "a" }, { item: "b"} and { item: "c" } in your child scope in ng-repeat. If you use ng-model="item", you are binding to this item on the new, projected object now and not to your original array.

You can however bind to a property: Change the ng-model="item" to something like ng-model="item.name" (http://jsfiddle.net/ya4ohauk/) and the binding should work as expected.

[
  { name: "a" },
  { name: "b" },
  ...
]

or in your case

[
  { name: "Campus 1" },
  { name: "Campus 2" }
]

If you need a flat array of these values, you can use JavaScripts map function to get these:

var flatArray = $scope.campuses.map(function(campus) {
  return campus.name;
});
over 1 year ago ·

@theatlasroom Now I understand. The reason why this is happening is because ng-repeat creates a new scope for every iteration. Therefore when the item is referenced, the data is lost upon refresh. Whereas referencing it directly from the list, which is stored in the controller's scope, will keep the data in sync. Even if you were to put campuses directly in scope, this wouldn't work. Like:

$scope.campuses = [];

This is all because of how $scope inheritance works. You can take a look at this: https://github.com/angular/angular.js/wiki/Understanding-Scopes

over 1 year ago ·

Greeeeat !!!!!

over 1 year ago ·