Mischa Mischa - 4 months ago 16
AngularJS Question

ng-repeat not updating on update of array

I'm rendering data through an ng-repeat cycle. And i would like it to update as I update the array. From what i read this should happen automatically however this is not working. So what am i doing wrong?

html:

<tr ng-repeat="data in dataDifferenceArray">
<td>
{{data.name}}
</td>
<td>
{{data.startData}}
</td>
<td>
{{data.endData}}
</td>
<td>
{{data.differenceData}}
</td>
</tr>


Controller (this function is triggered on a button with ng-click):

$scope.getDifferences = function () {
$scope.dataDifferenceArray = [];
var i;
for (i = 0; i < 20 ;i++) {
$scope.dataDifferenceArray.push({
name : 30,
startData : 20,
endData : 2,
differenceData : 30
})
}
}


Console.log reveals that the array is updated correctly, however the table in my view does not change. I don't know what i'm doing wrong.

Answer

That's because you change the array reference in your method getDifferences.

To avoid that, us dot, for example with "controller as" syntax:

<div ng-controller="myController as c">
    [...]

    <tr ng-repeat="data in c.dataDifferenceArray">
        <td>
          {{data.name}}
        </td>
        <td>
          {{data.startData}}
        </td>
        <td>
          {{data.endData}}
        </td>
        <td>
          {{data.differenceData}}
        </td>
    </tr>
    [...]

If you want to understand how scopes work, i would advice this article : https://github.com/angular/angular.js/wiki/Understanding-Scopes#ngRepeat

Another solution could be :

$scope.getDifferences = function () {
  $scope.dataDifferenceArray.length = 0; // here ----
  var i;
  for (i = 0; i < 20 ;i++) {
    $scope.dataDifferenceArray.push({
      name : 30,
      startData : 20,
      endData : 2,
      differenceData : 30
    })
  }
}

but in this solution, you need to create the array outside (and only once) : $scope.dataDifferenceArray = [];

Edit : To understand your problem :

  1. At start you have $scope.dataDifferenceArray = VALUE (VALUE = your initialisation)

  2. You give to the ng-repeat the reference to this dataDifferenceArray (let's call it REFERENCE1).

  3. Now if you call getDifferences, it will execute : $scope.dataDifferenceArray = []; which is equivalent to $scope.dataDifferenceArray = new Array();. So $scope.dataDifferenceArray has a new reference (let's call it REFERENCE2).

  4. The ng-repeat still has the reference REFERENCE1 (see edit2). So it won't know the changes.

  5. If we use a dot, ng-repeat will focus the property dataDifferenceArray of the object c. So even if we change the reference of the property dataDifferenceArray that's no more a problem for ng-repeat.

Edit2: My answer was not really clear, let's try to understand what is happening in deep:

Q: Why does the ng-repeat still has the reference REFERENCE1 ?

You have to remember that there is not only 1 scope in your template.

Eg: the ng-repeat directive create new scopes for each of the repeated elements, but we can still access to the parent scope in each child scope. Angular implemented this behavior using Prototype Inheritance: each child scope inherit the properties of its parent scope thanks to its prototype.

You can experiment how it is working by inspecting one on your child elements, then enter in the console: $($0).scope() (it will give you the scope of the selected element, $0 is the selected element (Chrome)). You can now see that there is the same object in $($0).scope().$parent and $($0).scope().__proto__, it is your parent scope.

But there is one problem with prototype inheritance: Let's say we have A = {}; B = {C: 1}, if A inherits from B then A.C == 1. But if we affect a new value A.C = 2, we did not change B, only A.

Angular expressions are evaluated using the current scope as this. So if we have something like ng-click="dataDifferenceArray = []" it is equivalent to this.dataDifferenceArray = [] with this being the scope of the element where ng-click is.

This problem is solved when you are using controller-as because it injects the controller in the scope and you will never directly affect a property to the scope.

Let's take back our example: A = {}; B = {C: {D: 1}}, if A inherits from B then A.C.D == 1. And now even if we affect a new value A.C.D = 2, we changed B also.