I am trying to allow the user to edit a list of items by using ngRepeat
and ngModel
. (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>
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
<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>
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
The problem seems to be in the way how ng-model
works with input
and overwrites the name
object, making it lost for ng-repeat
.
As a workaround, one can use the following code:
<div ng-repeat="name in names">
Value: {{name}}
<input ng-model="names[$index]">
</div>
Hope it helps
I tried the solution above for my problem at it worked like a charm. Thanks!
http://jsfiddle.net/leighboone/wn9Ym/7/
Here is my version of that:
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.models = [{
name: 'Device1',
checked: true
}, {
name: 'Device1',
checked: true
}, {
name: 'Device1',
checked: true
}];
}
and my HTML
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<h1>Fun with Fields and ngModel</h1>
<p>names: {{models}}</p>
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th>Feature 1</td>
<th>Feature 2</th>
<th>Feature 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Device</td>
<td ng-repeat="modelCheck in models" class=""> <span>
{{modelCheck.checked}}
</span>
</td>
</tr>
<tr>
<td>
<label class="control-label">Which devices?</label>
</td>
<td ng-repeat="model in models">{{model.name}}
<input type="checkbox" class="checkbox inline" ng-model="model.checked" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
how do something like:
<select ng-model="myModel($index+1)">
And in my inspector element be:
<select ng-model="myModel1">
...
<select ng-model="myModel2">
来源:https://stackoverflow.com/questions/13714884/difficulty-with-ng-model-ng-repeat-and-inputs