Question

I am getting, what I believe to be, an odd behaviour when adding arrays to an object in an ng-repeat.

Due to the "Duplicates in a repeater are not allowed" error I am using track by $index but when I try to show / hide a row everything that was added is toggled. I have tried passing the index into an ng-click and even setting the value directly in the ng-click

Here is some sample code and a jsFiddle demonstration

HTML excerpt:

<tbody ng-repeat="person in object track by $index" ng-show="person.visible">
<tr>
    <td>{{ person.name }}</td>
    <td>{{ person.age }}</td>
    <td>
        <a href ng-click="togglePerson($index)">Hide</a>
    </td>
    <!-- <td>
        <a href ng-click="person.visible = !person.visible">Hide</a>
    </td> -->
</tr>
</tbody>

Javascript:

myApp.controller('myController', function ($scope) {
    $scope.init = function () {
        $scope.object = [{
            name: "Billy",
            age: 21,
            visible: true
        }];
        $scope.newEntry = {
            name: 'Ralph',
            age: 16,
            visible: true
        };
    };

    $scope.addPerson = function () {
        $scope.object.push($scope.newEntry);
    };

    $scope.togglePerson = function (index) {
        console.log(index); // Index is being passed properly
        $scope.object[index].visible = !$scope.object[index].visible;
    };
});

I have done quite a few visibility toggles in the past within a repeater (never with a track by $index) but I can't seem to determine where I went wrong. Is this even related to the tracking / array comparison?


Edit

To be clear, my demonstration is not how I am using this code. I am actually toggling a second rows visibility. I know that my demonstration is flawed since once you toggle hidden you can't toggle back.

Was it helpful?

Solution

When you push an object into an array it pushes the object by reference. Each time you call this

$scope.addPerson = function () {
        $scope.object.push($scope.newEntry);
    };

You are basically pushing the same object at different index positions of the array. Now this same object is repeated over in the repeater.

And when you toggle :

$scope.togglePerson = function (index) {
        console.log(index); // Index is being passed properly
        $scope.object[index].visible = !$scope.object[index].visible;
    };

you are basically setting the same objects property on all the array indexes, since the array has the same object at all the indexes.

You need to do a copy of the object before pushing it into the array so that you get a new object every single time.

$scope.addPerson = function () {
        $scope.object.push(angular.copy($scope.newEntry));
    };

Here is a fiddle that demonstrates this :

jsFiddle demonstration

NOTE : This has nothing to do with array track by $index

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top