[angularjs] Difficulty with ng-model, ng-repeat, and inputs

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 question is related to angularjs angularjs-ng-repeat angularjs-ng-model

The answer is


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 names array => `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.


how do something like:

<select ng-model="myModel($index+1)">

And in my inspector element be:

<select ng-model="myModel1">
...
<select ng-model="myModel2">

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>

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 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


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>

Examples related to angularjs

AngularJs directive not updating another directive's scope ERROR in Cannot find module 'node-sass' CORS: credentials mode is 'include' CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response WebSocket connection failed: Error during WebSocket handshake: Unexpected response code: 400 Print Html template in Angular 2 (ng-print in Angular 2) $http.get(...).success is not a function Angular 1.6.0: "Possibly unhandled rejection" error Find object by its property in array of objects with AngularJS way Error: Cannot invoke an expression whose type lacks a call signature

Examples related to angularjs-ng-repeat

Angular ng-repeat add bootstrap row every 3 or 4 cols ng-if, not equal to? Understanding the ngRepeat 'track by' expression Calculating sum of repeated elements in AngularJS ng-repeat Angular.js ng-repeat filter by property having one of multiple values (OR of values) Using ng-if as a switch inside ng-repeat? In Angular, how to pass JSON object/array into directive? how to split the ng-repeat data with three columns using bootstrap Preserve line breaks in angularjs ng-repeat: access key and value for each object in array of objects

Examples related to angularjs-ng-model

Angular CLI - Please add a @NgModule annotation when using latest ng-model for `<input type="file"/>` (with directive DEMO) AngularJS: ng-model not binding to ng-checked for checkboxes filters on ng-model in an input Difficulty with ng-model, ng-repeat, and inputs