Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using ng-model within nested ng-repeat directives

I'm trying to use ng-model "within" a ng-repeat directive that is itself nested in a ng-repeat directive.

The following jsfiddle demonstrates my problem and my two attempts to solve it.

http://jsfiddle.net/rskLy/4/

My Controller is defined as follows:

var mod = angular.module('TestApp', []);
mod.controller('TestCtrl', function ($scope) {        
var machine = {};
machine.noteMatrix = [
    [false, false, false],
    [false, true, false],
    [false, false, false]
];

$scope.machine = machine;

// ...
});

1.

<table>
    <thead>
        <tr>
            <th>--</th>
            <th ng-repeat="no in machine.noteMatrix[0]">{{$index+1}}</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="track in machine.noteMatrix">
            <td>--</td>                
            <td ng-repeat="step in track">                    
                <input type="checkbox" ng-model="track[$index]"> {{step}}
            </td>
        </tr>
    </tbody>
</table>

The first example/attempt updates the machine.noteMatrix inside the controller, but everytime a checkbox is pressed, angularjs displays the following error twice in the javascript console:

Duplicates in a repeater are not allowed. Repeater: step in track

and sometimes it will also display this error:

Duplicates in a repeater are not allowed. Repeater: no in machine.noteMatrix[0]

2.

<table>
    <thead>
        <tr>
            <th>--</th>
            <th ng-repeat="no in machine.noteMatrix[0]">{{$index+1}}</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="track in machine.noteMatrix">
            <td>--</td>                
            <td ng-repeat="step in track">                    
                <input type="checkbox" ng-model="step"> {{step}}
            </td>
        </tr>
    </tbody>
</table>

The second example/attempt reads the data correctly from the noteMatrix and no errors are displayed in the javascript console when checking/unchecking the checkboxes. However changing their states is not updating the machine.noteMatrix in the controller (press the "Show Matrix" button to see the matrix in the jsfiddle).

Can anyone shed a light on this? :)

like image 946
Óli Fly Avatar asked Apr 12 '13 14:04

Óli Fly


People also ask

What can I use instead of NG-repeat?

The *ngFor directive in Angular is similar to the ng-repeat directive in AngularJS. It repeats the associated DOM element for each item in the specified collection.

What is the difference between ng-repeat and Ng options?

ng-repeat creates a new scope for each iteration so will not perform as well as ng-options. For small lists, it will not matter, but larger lists should use ng-options. Apart from that, It provides lot of flexibility in specifying iterator and offers performance benefits over ng-repeat.

What is difference between ng-model and Ng bind?

ngModel usually use for input tags for bind a variable that we can change variable from controller and html page but ngBind use for display a variable in html page and we can change variable just from controller and html just show variable.

Does ng-repeat create a new scope?

Each iteration of ng-repeat creates a new child scope, and that new child scope always gets a new property.


3 Answers

This is a common issue for people in Angular. What's happening is ng-repeat creates it's own scope, and if you pass an array of value types into it (such as an array of booleans), updating them will not update the parent scope. You need to pass an array of reference types to the ng-repeat and update those in order for it to persist:

Here is a solution showing this based off of your fiddle

machine.noteMatrix = [
    [
        { value: false },
        { value: false }, 
        { value: false }
    ],
    [
        { value: false },
        { value: true }, 
        { value: false }
    ],
    [
        { value: false },
        { value: false }, 
        { value: false }
    ]
];

It's ugly, I know, but the alternative is uglier. You'd need to do something to manage your own loop and reference the values via the $parent or $parent.$parent object. I don't recommend this.

like image 184
Ben Lesh Avatar answered Oct 20 '22 15:10

Ben Lesh


Your first solution seems to be correct.

This appears to be a bug, introduced in the unstable branch of angularJS (you are using 1.1.4, which is unstable - the stable version, 1.0.6, works as expected)

EDIT:

Turns out this isn't a bug, but a new feature - the ngRepeat directive now allows for a tracking function to be defined (associating the model's id with the DOM element), and no longer allows for these tracking variables to be repeated. See the corresponding commit, the docs for 1.1.4 on ngRepeat and the changelog

like image 25
Tiago Roldão Avatar answered Oct 20 '22 16:10

Tiago Roldão


You don't need to alter your model or access the $parent. What's missing is "track by $index":

    <tr ng-repeat="track in machine.noteMatrix">
        <td>--</td>                
        <td ng-repeat="step in track track by $index">                    
            <input type="checkbox" ng-model="track[$index]"> {{step}}
        </td>
    </tr>

Here it is in yr fiddle.

More information: Angular ng-repeat dupes

I'm not sure if track by existed yet when the question was asked, so the other answers may have been correct at the time, but in current Angular this is the way to go.

like image 43
Semicolon Avatar answered Oct 20 '22 16:10

Semicolon