Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular $watch not working

I'm using two $watches on my controller that are supposed to take an eye at these two objects:

$scope.gastos = {
    name: "Gastos mensuales",
    data: [0,0,0,0,0,0,0,0,0,0,0,0],
    labels: ["Enero", "Febrero", "Marzo", "Abril", "Mayo",
                "Junio", "Julio", "Agosto", "Septiembre", "Octubre",
                "Noviembre", "Diciembre"]
};

$scope.ganancias = {
    name: "Ganancias mensuales",
    data: [0,0,0,0,0,0,0,0,0,0,0,0],
    labels: ["Enero", "Febrero", "Marzo", "Abril", "Mayo",
                "Junio", "Julio", "Agosto", "Septiembre", "Octubre",
                "Noviembre", "Diciembre"]
};

Two charts (from the Charts.js and angular-charts plugins) read data from them. I've put the charts in custom directives that receive the data from an attribute, and they're working properly.

The problem is, that I want to create another chart who reads another object that is equal to these, but calculates it's data in this method:

function calcularBeneficios(){
 var data = [];
 for(var i=0;i<12;i++){
    data[i] = $scope.ganancias.data[i] - $scope.gastos.data[i];
 }
    console.log("FUNCTION DATA: "+data);
 return data;
}

These are the watches (I tried both watching the object and the object.data variable):

$scope.$watch("gastos", function(){
  $scope.beneficios.data = calcularBeneficios();
  console.log("SCOPE: "+$scope.beneficios.data);
});

$scope.$watch("ganancias", function(){
  $scope.beneficios.data = calcularBeneficios();
  console.log("SCOPE: "+$scope.beneficios.data);
});

This doesn't work. You see all the console.logs? I only see the "SCOPE" console.log, once (not even twice for ganancias). When I change the data in some inputs that are bound to these two objects, everything works (the charts get real-time updated) but the beneficios chart doesn't, aswell these watches just don't work.

Am I doing something wrong at these two watches?

like image 212
Zerok Avatar asked Feb 25 '16 09:02

Zerok


People also ask

What is $Watch in angular?

What is the angular JS watch function? The angular JS $watch function is used to watch the scope object. The $watch keep an eye on the variable and as the value of the variable changes the angular JS $what runs a function. This function takes two arguments one is the new value and another parameter is the old value.

How does $Watch work in AngularJS?

When you create a data binding from somewhere in your view to a variable on the $scope object, AngularJS creates a "watch" internally. A watch means that AngularJS watches changes in the variable on the $scope object. The framework is "watching" the variable. Watches are created using the $scope.


2 Answers

The problem is is that you are watching the top-level gastos and ganancias objects. There are two potential issues with this:

  1. As the docs for $watch say, by default, normal JavaScript inequality is used to determine if an object has changed (x !== y). As the object itself is the same object, you are just changing data within it, this will always be true, so the $watch is not fired.

  2. You can get around 1. by setting the objectEquality flag to true ($scope.$watch('x', function(){}, true)). This then does a deep comparison and should pick up differences in the data array. However this is much more performance intensive.

As you are just using the data array in each object, you can simply use $scope.$watchCollection on gastos.data. This is cleaner and faster.

Here is a working snippet demonstrating this:

angular.module("myApp", []).controller('myCtrl', function($scope) {

  // same code
  function calcularBeneficios() {
    var data = [];
    for (var i = 0; i < 12; i++) {
      data[i] = $scope.ganancias.data[i] - $scope.gastos.data[i];
    }
    console.log("FUNCTION DATA: " + data);
    return data;
  }

  
  $scope.beneficios = {
    name: "Beneficios mensuales",
    data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    labels: ["Enero", "Febrero", "Marzo", "Abril", "Mayo",
      "Junio", "Julio", "Agosto", "Septiembre", "Octubre",
      "Noviembre", "Diciembre"
    ]
  };
  
  $scope.gastos = {
    name: "Gastos mensuales",
    data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    labels: ["Enero", "Febrero", "Marzo", "Abril", "Mayo",
      "Junio", "Julio", "Agosto", "Septiembre", "Octubre",
      "Noviembre", "Diciembre"
    ]
  };

  $scope.ganancias = {
    name: "Ganancias mensuales",
    data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    labels: ["Enero", "Febrero", "Marzo", "Abril", "Mayo",
      "Junio", "Julio", "Agosto", "Septiembre", "Octubre",
      "Noviembre", "Diciembre"
    ]
  };

  // watch the array collection in gastos.data 
  $scope.$watchCollection("gastos.data", function() {
    $scope.beneficios.data = calcularBeneficios();
    console.log("SCOPE: " + $scope.beneficios.data);
  });

  // watch the array collection in ganancias.data 
  $scope.$watchCollection("ganancias.data", function() {
    $scope.beneficios.data = calcularBeneficios();
    console.log("SCOPE: " + $scope.beneficios.data);
  });
  
  
  // testing functions

  $scope.changeGastos = function() {
    $scope.gastos.data[4] = 10;
  }
  
  $scope.changeGanancias = function() {
    $scope.ganancias.data[0] = 40;
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myCtrl">
  <h3>beneficios.data</h3>
  <ul>
    <li ng-repeat="i in beneficios.data track by $index">{{i}}</li>
  </ul>
  <button ng-click="changeGastos()">Change Gastos</button>
  <button ng-click="changeGanancias()">Change Ganancias</button>
</div>
like image 134
Rhumborl Avatar answered Oct 05 '22 03:10

Rhumborl


Use $watchCollection instead of $watch to watch for a collection.

Or you can also watch like this:

$scope.$watch("gastos + ganancias", function(){
  $scope.beneficios.data = calcularBeneficios();
  console.log("SCOPE: "+$scope.beneficios.data);
});
like image 26
Shashank Agrawal Avatar answered Oct 05 '22 04:10

Shashank Agrawal