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?
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.
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.
The problem is is that you are watching the top-level gastos
and ganancias
objects. There are two potential issues with this:
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.
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>
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);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With