Difficulty with ng-model, ng-repeat, and inputs
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/
Using Angular latest version (1.2.1) and track by $index
. This issue is fixed
http://jsfiddle.net/rnw3u/53/
<div ng-repeat="(i, name) in names track by $index">
Value: {{name}}
<input ng-model="names[i]">
</div>
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.
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>