Difficulty with ng-model, ng-repeat, and inputs
Solution 1
This seems to be a binding issue.
The advice is don't bind to primitives.
Your ngRepeat
is iterating over strings inside a collection, when it should be iterating over objects. To fix your problem
<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
<h1>Fun with Fields and ngModel</h1>
<p>names: {{models}}</p>
<h3>Binding to each element directly:</h3>
<div ng-repeat="model in models">
Value: {{model.name}}
<input ng-model="model.name">
</div>
jsfiddle: http://jsfiddle.net/jaimem/rnw3u/5/
Solution 2
Using Angular latest version (1.2.1) and track by $index
. This issue is fixed
<div ng-repeat="(i, name) in names track by $index">
Value: {{name}}
<input ng-model="names[i]">
</div>
Solution 3
You get into a difficult situation when it is necessary to understand how scopes, ngRepeat and ngModel with NgModelController work. Also try to use 1.0.3 version. Your example will work a little differently.
You can simply use solution provided by jm-
But if you want to deal with the situation more deeply, you have to understand:
- how AngularJS works;
- scopes have a hierarchical structure;
- ngRepeat creates new scope for every element;
- ngRepeat build cache of items with additional information (hashKey); on each watch call for every new item (that is not in the cache) ngRepeat constructs new scope, DOM element, etc. More detailed description.
- from 1.0.3 ngModelController rerenders inputs with actual model values.
How your example "Binding to each element directly" works for AngularJS 1.0.3:
- you enter letter
'f'
into input; -
ngModelController
changes model for item scope (names array is not changed) =>name == 'Samf'
,names == ['Sam', 'Harry', 'Sally']
; -
$digest
loop is started; -
ngRepeat
replaces model value from item scope ('Samf'
) by value from unchanged names array ('Sam'
); -
ngModelController
rerenders input with actual model value ('Sam'
).
How your example "Indexing into the array" works:
- you enter letter
'f'
into input; -
ngModelController
changes item in namesarray
=> `names == ['Samf', 'Harry', 'Sally']; - $digest loop is started;
-
ngRepeat
can't find'Samf'
in cache; -
ngRepeat
creates new scope, adds new div element with new input (that is why the input field loses focus - old div with old input is replaced by new div with new input); - new values for new DOM elements are rendered.
Also, you can try to use AngularJS Batarang and see how changes $id of the scope of div with input in which you enter.
Solution 4
If you don't need the model to update with every key-stroke, just bind to name
and then update the array item on blur event:
<div ng-repeat="name in names">
Value: {{name}}
<input ng-model="name" ng-blur="names[$index] = name" />
</div>
Solution 5
I just updated AngularJs to 1.1.2 and have no problem with it. I guess this bug was fixed.
http://ci.angularjs.org/job/angular.js-pete/57/artifact/build/angular.js
Related videos on Youtube
Nick Heiner
JS enthusiast by day, horse mask enthusiast by night. Talks I've Done
Updated on July 08, 2022Comments
-
Nick Heiner almost 2 years
I am trying to allow the user to edit a list of items by using
ngRepeat
andngModel
. (See this fiddle.) However, both approaches I've tried lead to bizarre behavior: one doesn't update the model, and the other blurs the form on each keydown.Am I doing something wrong here? Is this not a supported use case?
Here is the code from the fiddle, copied for convenience:
<html ng-app> <head> <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"> </head> <body ng-init="names = ['Sam', 'Harry', 'Sally']"> <h1>Fun with Fields and ngModel</h1> <p>names: {{names}}</p> <h3>Binding to each element directly:</h3> <div ng-repeat="name in names"> Value: {{name}} <input ng-model="name"> </div> <p class="muted">The binding does not appear to be working: the value in the model is not changed.</p> <h3>Indexing into the array:</h3> <div ng-repeat="name in names"> Value: {{names[$index]}} <input ng-model="names[$index]"> </div> <p class="muted">Type one character, and the input field loses focus. However, the binding appears to be working correctly.</p> </body> </html>
-
Mark Rajcok over 11 yearsThis is very similar to stackoverflow.com/questions/12977894/…, but the 2nd approach is new here, so it is not exactly a duplicate. I provided a detailed answer (similar to Artem's) there, explaining how ng-repeat child scopes work.
-
vucalur over 10 yearsSince it cost me reasonable amount of googling before finally finding this thread, may I rename it to sth like: "Parent Model not updated from ngRepeat inputs" or "Model not updated when using ngRepeat" or "ngRepeat inputs not bound to (parent) model"? Maybe you have better ideas for the title ?
-
-
Mark Rajcok over 11 yearsThanks for the first hyperlink, as it explains the blur/loss of focus/flicker issue. I always wondered why that happened.
-
Mark Rajcok over 11 yearsthanks for the 1.0.3 explanation. It is interesting that in 1.0.3, the "bind directly" case, the input field appears to be broken, in the sense that it doesn't appear to accept any changes/typed input (at least in Chrome). I'm sure we'll see quite a few SO posts about this in the near future :) I suppose this new way is better, as it will be more obvious that something is wrong.
-
Daniel Gruszczyk almost 10 yearsthanks for that, helped alot. Still, binding to primitives should be there imo...
-
Stefanos Chrs over 9 yearsold post but thnx, took me some time to find the 'not changing the model inside ngRepeat' problem and it was your advice not to bind to primitives
-
Dan Ochiana almost 9 yearsdid the job; no more reference loss
-
Andrea almost 8 yearsI did search so long for that.... so easy... just so hidden. Give this as the answer!
-
z0r almost 8 yearsAlso: Don't modify the entire list while typing - a trap I fell into. I was watching the collection for changes, and replacing it with an identical copy - so even though I wasn't binding to primitives, the elements were being recreated.
-
Alex Coroza over 7 yearsoooooh, so adding "track by $index" in ng-repeat also fixes the "flickering" issue
-
Draško Kokić almost 7 yearsWhy not using
ng-model="names[$index]"
... I know it's a workaround, but it works ;-)