Angular JS $watch on service object - angularjs

I've searched high and low, tried many 'solutions' and cannot get angular to watch a service correctly.
Service:
self = {}; // or self = this;
self.testVar = null;
$interval(function() {
self.testVar = Math.random();
}, 1000);
return self;
Controller:
$scope.testVar = myService.testVar;
$scope.$watch('testVar', function(newVal, oldVal) {
console.log(newVal, oldVal);
})
This code doesn't work. I'd like to avoid using $broadcast as it seems like angular is supposed to handle service updates magically. Any insights are appreciated.
EDIT: Solution in controller:
$scope.$watch(
function() { return myService.testVar },
function(newVal) {
console.log(newVal);
}
)
If anyone cares to explain this behaviour, it would benefit those experiencing the same problem.
Upon further investigation, changing the $interval timer to 100ms still results in the controller printing to the console every 1000ms which suggests $scope.$watch is set at a max rate of 1 second. I suppose it's best to use $broadcast or a observer/callback method instead O_o

$scope.testVar = myService.testVar;
$scope.$watch('testVar', function(newVal, oldVal) {
console.log(newVal, oldVal);
})
In this code you set $scope.testVar from the service. Then you watch it. Nothing changes it again. In your "solution", it works because you are watching the "live" value from the service. You do not need to use $broadcast or observer or callbacks. This will work as expected when you are watching the service value and updating the scope.

Related

Auto trigger function throuigh service in angularjs

hi all i am using angulrajs passing one value from one controller to another controller using service it's work fine but my need is when service value change in controller 2 i get the service value in one scope when scope value change i need trigger the function it's called refresh function when service value change and that i need to call the refresh function here my fiddle
https://jsfiddle.net/ctawL4t3/10/
You can just $watch your value.storeObject. Though it's not best of the practices, but it suits this kind of feature.
$scope.$watch('value.storedObject', function(newVal) {
if(newVal !== '') {
refresh()
}
})
working fiddle (open console to see refresh function logging)
You can try to use angular default $emit, $broadcast, or try to do 2 simple functions in own service
angular.module('app').factory('StoreService', function() {
var listeners = {};
var emit = function(name, val) {
if(listeners[name]) {
listeners[name](val)
}
}
var on = function(name, callback) {
listeners[name] = callback;
}
return {
emit: emit,
on: on,
storedObject: ''
};
});
JSFiddle example
JSFiddle example $watch
JSFiddle example ng-change is better because, you can use easily debounce
you can use broadcast function for that
Please check this SO link to find the related answer
How to call a function from another controller in angularjs?
app.controller('One', ['$scope', '$rootScope'
function($scope) {
$rootScope.$on("CallParentMethod", function(){
$scope.parentmethod();
});
$scope.parentmethod = function() {
// task
}
}
]);
app.controller('two', ['$scope', '$rootScope'
function($scope) {
$scope.childmethod = function() {
$rootScope.$emit("CallParentMethod", {});
}
}
]);

AngularJS - Watch service changes not updating view

Im working on angularjs 1.4. Im trying to have some frontend-cache collection that updates the view when new data is inserted. I have checked other answers from here Angularjs watch service object but I believe Im not overwriting the array, meaning that the reference is the same.
The code is quite simple:
(function(){
var appCtrl = function($scope, $timeout, SessionSvc){
$scope.sessions = {};
$scope.sessions.list = SessionSvc._cache;
// Simulate putting data asynchronously
setTimeout(function(){
console.log('something more triggered');
SessionSvc._cache.push({domain: "something more"});
}, 2000);
// Watch when service has been updated
$scope.$watch(function(){
console.log('Watching...');
return SessionSvc._cache;
}, function(){
console.log('Modified');
}, true);
};
var SessionSvc = function(){
this._cache = [{domain: 'something'}];
};
angular.module('AppModule', [])
.service('SessionSvc', SessionSvc)
.controller('appCtrl', appCtrl);
})();
I thought that the dirty checking would have to catch the changes without using any watcher. Still I put the watcher to check if anything gets executed once the setTimeout function is triggered. I just dont see that the change is detected.
Here is the jsbin. Im really not understanding sth or doing a really rockie mistake.
You need to put $scope.$apply(); at the bottom of your timeout to trigger an update. Alternatively you can use the injectable $timeout service instead of setTimeout and $apply will automatically get called.
jsbin

Angularjs watchGroup() not working with Service changes

I'm having problems to watch multiple variables in a Service when some changes are made on it by other controllers.
I've the following:
angular.module('carApp').controller('CarListCtrl', function ($scope, CarService) {
$scope.carList = CarService.carList
$scope.initalAmount = CarService.initialAmount
$scope.startDate = CarService.startDate
$scope.$watchGroup(['carList', 'initialAmount', 'startDate'],
function (newValues, oldValues, $scope) {
console.log(newValues);
});
});
Other controllers update the values in the Service all the time but the watchGroup never fires up...
I've create a simple watch targeting the service directly to check if work, and it's working ..., so I imagine that the watchGroup should target the service variable directly but I can't find how to do that....
here is the simple watch that works:
$scope.$watch(function () {
return CarService.carList
}, function (newVal, oldVal) {
console.log(newVal);
});
What should I do to make it works with the multiple service variables?
UPDATE 1:
Just a update... if I try the watchgroup with just one element, for example $scope.$watchGroup(['carList'], ... it works, so I tried with each one and it works every time, but as soon as I add one more element it stop working... very annoying...
Tks again guys!
Just to close this, people from angularjs github help me out: here is the anwser for anyone who need:
Each value inside a watchGroup array can be a expression or a function, so you can use three different functions inside your watchGroup. http://plnkr.co/edit/nMmPt808xAFXqjJ6yEoc?p=preview
$scope.$watchGroup([
function() {
return myService.valueOne()
},
function() {
return myService.valueTwo()
}
], function(newValues, oldValues) {
$scope.valueOne = newValues[0]
$scope.valueTwo = newValues[1]
})
Your first example is possibly not working because your other controllers assign new values to initialAmount and startDate in your service, which means you have different objects with different values in your controller and your startDate. It´probably works with the carList, because you are only adding / removing items, which means it remains the same object in your controller and your service.

Angular: transfer data with $scope

Really have no idea why this doesn't work. I must be doing something incredibly stupid.
Here is a controller:
angular.module('nightlifeApp')
.controller('TestCtrl', function($scope) {
$scope.testvar = 'before';
setTimeout(function() {
$scope.testvar = 'after';
}, 2000);
});
and here is the view that has this as the controller:
h1(ng-bind='testvar')
h1 {{testvar}}
But neither h1 element ever changes! Any thoughts?
If you're using setTimeout then you manually need to trigger apply. Like
$apply() is used to execute an expression in angular from outside of
the angular framework. (For example from browser DOM events,
setTimeout, XHR or third party libraries). Because we are calling into
the angular framework we need to perform proper scope life cycle of
exception handling, executing watches.
setTimeout(function() {
$scope.$apply(function() {
$scope.testvar = 'after';
});
}, 2000);
In my opinion you should use $timeout service. So it will trigger $apply() automatically. Your code will look like
$timeout(function () {
$scope.testvar = 'after';
}, 2000);
Make sure you've injected $timeout service in your controller. In your HTML you don't need to use ng-bind. You're doing same in controller. Only
<h1> {{testvar}} </h1>

angular $scope.$watch for array

According to this: How to deep watch an array in angularjs? it should work but i dont know what i am doing wrong...
$scope.$watch('statuses', function(newVal, oldValue) {
if (oldValue==newVal) return false;
console.log("st changed");
},true);
And let's say i somehow asynchronly update the $scope.statuses for example after 3seconds
setTimeout(function() {
$scope.statuses.push("ahhh");
},3000);
And nothing happens...any ideas why please?
Use the following code:
setTimeout(function() {
$scope.$apply(function () {
$scope.statuses.push("ahhh");
}
},3000);
You have to use $apply since AngularJS is unaware of update to $scope
As a convenience, AngularJS provides $timeout, which is like setTimeout, but automatically wraps your code in $apply by default.
$scope.statuses = [];
$scope.$watch('statuses', function(newVal, oldValue) {
console.log(newVal, oldValue);
if (oldValue==newVal) return false;
console.log("st changed");
},true);
setTimeout(function() {
$scope.statuses.push("ahhh");
$scope.$apply();
},3000);
$apply function serves for notification of update to $scope.
live sample
http://plnkr.co/edit/LUjESyZOPfIALE2q5YYi?p=preview
if statuses will host just primitive values, better use $watchCollection
Use $timeout which is the Angular version of setTimeout. When you use $timeout, AngularJS rebinds everything after the callback.
Having to manually call $apply is always a sign that you did not do something the angular way. It's a way to tell Angular: I've done something behind your back; please refresh.

Resources