Andrzej Rehmann
Andrzej Rehmann

Reputation: 13790

ng-model binding not updates another ng-model on the same object

I have this code

http://plnkr.co/edit/aycnNVoD96UMbsC7rFmg?p=preview

<div data-ng-app="" data-ng-init="names=['One']">

<input type="text" ng-model="names[0]">

  <p>Looping with ng-repeat:</p>
  <ul>
    <li data-ng-repeat="name in names">
      <input type="text" ng-model="name"> {{ name }}
    </li>
  </ul>

</div>

When i change value of name[0] in the first input box it changes values of the second input box. But when i change value of name[0] in the second input box it does not change value of the first input box. Why?

Upvotes: 1

Views: 271

Answers (4)

ms87
ms87

Reputation: 17492

This is due to ng-repeat creating a child scope, so the reference to name inside the ng-repeat is different to that original one in the names array, see here:

New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view and ng-include all create new child scopes, so the problem often shows up when these directives are involved. (See this example for a quick illustration of the problem.)

Regarding as to why this happens, when you bind the input to name in names inside the ng-repeat, you are creating a new property on the new child scope created by the ng-repeat called name, and thus the ng-model of the textbox created by the ng-repeat is referencing a different name to that of the actual 0th element of the names array. As others have pointed out, if you use names[$index] you are implicitly referencing the 0th element of the names array, thus NOT creating a new name property on the child scope created by the ng-repeat. An angular best practice is not to have ng-models bound to primitives, rather objects, Sandy has mentioned in his answer if you bind to an object you will overcome this, and the 2 other posters have answered this by using $index to refer to the 0th element of the names array. This is one of the nucances of scope inheritance in angular.

A couple more handy links:

Here and here.

Upvotes: 1

Shreejibawa
Shreejibawa

Reputation: 1868

You need to provide $index in your ng-model.

<li data-ng-repeat="name in names">
    <input type="text" ng-model="names[$index]"> {{ name }}
</li>

You are binding ng-model="names[0]". So it means that you are binding value on first index of names array.

So when we write ng-model="names[$index]" in ng-repeat it means that all values will be bound accordingly into array. $index is an iterator offset of the repeated element.

names[0] = 'One'
names[1] = 'Two'

and so on!

Upvotes: 1

Sandy
Sandy

Reputation: 11687

Just wanted to give my bit on this. Somewhat related to your problem as I see.

<body>
<div data-ng-app="" data-ng-init="names=[{value:'One'}, {value:'Two'}]">
  <p>Looping with ng-repeat:</p>
  <ul>
    <li data-ng-repeat="name in names">
      <input type="text" ng-model="name.value"> {{ name }}
    </li>
  </ul>
</div>
</body>

Instead of binding the array item directly to the control, I would prefer to create an object of the array and then bind value of each item. This way we can avoid reference problems.

A working prototype jsfiddle

Hope it helps.

Upvotes: 1

Mualki
Mualki

Reputation: 186

It works if you bind your second input to : names[$index]

<input type="text" ng-model="names[$index]"> {{ name }}

Upvotes: 2

Related Questions