35

I'm trying to watch for changes in a service from a controller. I tried various things based on many qns here on stackoverflow, but I've been unable to make it work.

html:

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
        <div ng-click="setFTag()">Click Me</div>
    </div> 
</div>

javascript:

var myApp = angular.module('myApp',[]);

myApp.service('myService', function() {
    this.tags = {
        a: true,
        b: true
    };


    this.setFalseTag = function() {
        alert("Within myService->setFalseTag");
        this.tags.a = false;
        this.tags.b = false;

        //how do I get the watch in MyCtrl to be triggered?
    };
});


myApp.controller('MyCtrl', function($scope, myService) {

    $scope.setFTag = function() {
        alert("Within MyCtrl->setFTag");
        myService.setFalseTag();
    };        

    $scope.$watch(myService.tags, function(newVal, oldVal) {
        alert("Inside watch");
        console.log(newVal);
        console.log(oldVal);
    }, true);

});

How do I get the watch to trigger in the Controller?

jsfiddle

sathishvj
  • 1,394
  • 2
  • 17
  • 28
  • See my answer in the question [here](http://stackoverflow.com/questions/20623715/angular-watch-does-not-any-fire-event-in-my-directive/20623766#20623766). Your syntax in your `watch` function isn't what you want, but it's still valid angular syntax, which is why you're not getting a log error. – Hylianpuffball Dec 18 '13 at 20:15

1 Answers1

76

Try to write $watch by this way:

myApp.controller('MyCtrl', function($scope, myService) {


    $scope.setFTag = function() {
       myService.setFalseTag();
    };        

    $scope.$watch(function () {
       return myService.tags;
     },                       
      function(newVal, oldVal) {
        /*...*/
    }, true);

});

Demo Fiddle

[EDIT]

Sometimes this way will not work especially if service has been updated from 3d party.

To make it work we must help to angular to fire digest cycle.

Here is an example:

On service side when we want update tags value write something like:

if($rootScope.$root.$$phase != '$apply' && $rootScope.$root.$$phase != '$digest'){
   $rootScope.$apply(function() {
     self.tags = true;
   });
 }
 else {
   self.tags = true;
  }
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • 11
    I had no idea you could use watch in this way. This is a game changer. So much win. – joseym Apr 11 '14 at 15:04
  • for me this way is better because you avoid spelling mistakes with variables. – Maxim Shoustin Jul 13 '14 at 20:30
  • 2
    @Phill I only added other test case: if you use for example Cordova plugin thats out of angular scope, the watcher doesn't work. People might copy/paste this example and will be surprised why it doesn't work. So I try to cover different cases – Maxim Shoustin Jul 13 '14 at 20:59
  • Can you explain a little bit why we have to use the return function? It worked perfectly but I don't really get it. – lvarayut Dec 01 '14 at 14:39
  • Why is there no `return` statement in the service? How does that work? – CodyBugstein Mar 25 '15 at 11:17
  • I had to use $rootScope.$apply to trigger a watched variable inside my controller after it is being changed inside a directive. – Azadrum Feb 06 '16 at 05:39