1

Story:
I created an array ($scope.mainArray) which will be presented in a <table> <tr> using ng-repeat like so:

+---+
| 1 |
+---+
| 2 |
+---+
| 3 |
+---+

Inside every each object is an array which is presented in <td> using ng-repeat like so:

+---+-----+-----+-----+
| 1 | 1-1 | 1-2 | 1-3 |
+---+-----+-----+-----+
| 2 | 2-1 | 2-2 | 2-3 |
+---+-----+-----+-----+
| 3 | 3-1 | 3-2 | 3-3 |
+---+-----+-----+-----+

Each <td> is a boolean variable. If true, background color of <td> will turn into green. Otherwise, default background.

Problem:
Whenever I turn 1 boolean to true, all <td>'s in that column turns green. The code $scope.mainArray[0].subArray[0].isGreen = true; which is expected to turn cell 1-1 to green is making both 2-1 and 3-1 green.

SSCCE:

Plunker: https://plnkr.co/edit/9q3PMO?p=preview

SO Snippet:

angular.module("App", [])

  .controller("Controller", function($scope) {
    var initSubArray = function() {
      var data = [{
        "value": 1,
        "label": 1,
        "isGreen": false
      }, {
        "value": 2,
        "label": 2,
        "isGreen": false
      }, {
        "value": 3,
        "label": 3,
        "isGreen": false
      }];

      return data;
    };

    var initMainArray = function() {
      var data = [{
        "value": 1,
        "label": 1
      }, {
        "value": 2,
        "label": 2
      }, {
        "value": 3,
        "label": 3
      }];

      return data;
    };

    var putSubArray = function() {
      var subArray = initSubArray();
      for (i = 0; i < $scope.mainArray.length; i++) {
        $scope.mainArray[i].subArray = subArray;
      }
    };

    $scope.init = function() {
      $scope.mainArray = initMainArray();
      putSubArray();
      $scope.mainArray[0].subArray[0].isGreen = true;
    };
  });
table {
  border-collapse: collapse;
}

td {
  border: solid;
}

.green {
  background-color: #00FF00;
}
<!DOCTYPE html>
<html>

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script>
</head>

<body ng-app="App" ng-controller="Controller" ng-init="init()">
  <table>
    <tr ng-repeat="foo in mainArray">
      <td>
        {{foo.label}}
      </td>
      <td ng-repeat="bar in foo.subArray" ng-class="{'green' : bar.isGreen}">
        {{foo.label}}-{{bar.label}}
      </td>
    </tr>
  </table>
</body>

</html>
Michael 'Maik' Ardan
  • 4,213
  • 9
  • 37
  • 60

3 Answers3

6

Just replace the below function, find code snippet. Additionally added angular.copy to avoid reference.
Demo

var putSubArray = function() {
    var subArray = initSubArray();
    for(i = 0; i < $scope.mainArray.length; i++ ) {
        $scope.mainArray[i].subArray = angular.copy(subArray);
    }
};
Srigar
  • 1,648
  • 3
  • 14
  • 28
3

The problem is that you only have one subArray object. Which you then have three references too. When you make a change you are changing the subArray which all the rows are using.

You could fix this by changing this:

var putSubArray = function() {
    var subArray = initSubArray();
    for (i = 0; i < $scope.mainArray.length; i++) {
        $scope.mainArray[i].subArray = subArray;
    }
};

to

var putSubArray = function() {
    for (i = 0; i < $scope.mainArray.length; i++) {
        var subArray = initSubArray();
        $scope.mainArray[i].subArray = subArray;
    }
};

or a tidier variant on that

Chris Charles
  • 4,406
  • 17
  • 31
0

The problem is that you are referencing your subArray object. That means that, when you change the subArray from an element inside mainArray, the original object is also being changed.

The subArray objects from the rest of the elements inside mainArray will be changed as well. To avoid this, I recommend you to use angular.copy inside your putSubArray function:

var putSubArray = function() {
    var subArray = initSubArray();
    for(i = 0; i < $scope.mainArray.length; i++ ) {
        $scope.mainArray[i].subArray = angular.copy(subArray);
    }
};

I also updated your Plunker

You can see a more detailed explanation here

Community
  • 1
  • 1
Icarus
  • 1,627
  • 7
  • 18
  • 32