AngularJS : How to watch service variables?

221,978

Solution 1

You can always use the good old observer pattern if you want to avoid the tyranny and overhead of $watch.

In the service:

factory('aService', function() {
  var observerCallbacks = [];

  //register an observer
  this.registerObserverCallback = function(callback){
    observerCallbacks.push(callback);
  };

  //call this when you know 'foo' has been changed
  var notifyObservers = function(){
    angular.forEach(observerCallbacks, function(callback){
      callback();
    });
  };

  //example of when you may want to notify observers
  this.foo = someNgResource.query().$then(function(){
    notifyObservers();
  });
});

And in the controller:

function FooCtrl($scope, aService){
  var updateFoo = function(){
    $scope.foo = aService.foo;
  };

  aService.registerObserverCallback(updateFoo);
  //service now in control of updating foo
};

Solution 2

In a scenario like this, where multiple/unkown objects might be interested in changes, use $rootScope.$broadcast from the item being changed.

Rather than creating your own registry of listeners (which have to be cleaned up on various $destroys), you should be able to $broadcast from the service in question.

You must still code the $on handlers in each listener but the pattern is decoupled from multiple calls to $digest and thus avoids the risk of long-running watchers.

This way, also, listeners can come and go from the DOM and/or different child scopes without the service changing its behavior.

** update: examples **

Broadcasts would make the most sense in "global" services that could impact countless other things in your app. A good example is a User service where there are a number of events that could take place such as login, logout, update, idle, etc. I believe this is where broadcasts make the most sense because any scope can listen for an event, without even injecting the service, and it doesn't need to evaluate any expressions or cache results to inspect for changes. It just fires and forgets (so make sure it's a fire-and-forget notification, not something that requires action)

.factory('UserService', [ '$rootScope', function($rootScope) {
   var service = <whatever you do for the object>

   service.save = function(data) {
     .. validate data and update model ..
     // notify listeners and provide the data that changed [optional]
     $rootScope.$broadcast('user:updated',data);
   }

   // alternatively, create a callback function and $broadcast from there if making an ajax call

   return service;
}]);

The service above would broadcast a message to every scope when the save() function completed and the data was valid. Alternatively, if it's a $resource or an ajax submission, move the broadcast call into the callback so it fires when the server has responded. Broadcasts suit that pattern particularly well because every listener just waits for the event without the need to inspect the scope on every single $digest. The listener would look like:

.controller('UserCtrl', [ 'UserService', '$scope', function(UserService, $scope) {

  var user = UserService.getUser();

  // if you don't want to expose the actual object in your scope you could expose just the values, or derive a value for your purposes
   $scope.name = user.firstname + ' ' +user.lastname;

   $scope.$on('user:updated', function(event,data) {
     // you could inspect the data to see if what you care about changed, or just update your own scope
     $scope.name = user.firstname + ' ' + user.lastname;
   });

   // different event names let you group your code and logic by what happened
   $scope.$on('user:logout', function(event,data) {
     .. do something differently entirely ..
   });

 }]);

One of the benefits of this is the elimination of multiple watches. If you were combining fields or deriving values like the example above, you'd have to watch both the firstname and lastname properties. Watching the getUser() function would only work if the user object was replaced on updates, it would not fire if the user object merely had its properties updated. In which case you'd have to do a deep watch and that is more intensive.

$broadcast sends the message from the scope it's called on down into any child scopes. So calling it from $rootScope will fire on every scope. If you were to $broadcast from your controller's scope, for example, it would fire only in the scopes that inherit from your controller scope. $emit goes the opposite direction and behaves similarly to a DOM event in that it bubbles up the scope chain.

Keep in mind that there are scenarios where $broadcast makes a lot of sense, and there are scenarios where $watch is a better option - especially if in an isolate scope with a very specific watch expression.

Solution 3

I'm using similar approach as @dtheodot but using angular promise instead of passing callbacks

app.service('myService', function($q) {
    var self = this,
        defer = $q.defer();

    this.foo = 0;

    this.observeFoo = function() {
        return defer.promise;
    }

    this.setFoo = function(foo) {
        self.foo = foo;
        defer.notify(self.foo);
    }
})

Then wherever just use myService.setFoo(foo) method to update foo on service. In your controller you can use it as:

myService.observeFoo().then(null, null, function(foo){
    $scope.foo = foo;
})

First two arguments of then are success and error callbacks, third one is notify callback.

Reference for $q.

Solution 4

Without watches or observer callbacks (http://jsfiddle.net/zymotik/853wvv7s/):

JavaScript:

angular.module("Demo", [])
    .factory("DemoService", function($timeout) {

        function DemoService() {
            var self = this;
            self.name = "Demo Service";

            self.count = 0;

            self.counter = function(){
                self.count++;
                $timeout(self.counter, 1000);
            }

            self.addOneHundred = function(){
                self.count+=100;
            }

            self.counter();
        }

        return new DemoService();

    })
    .controller("DemoController", function($scope, DemoService) {

        $scope.service = DemoService;

        $scope.minusOneHundred = function() {
            DemoService.count -= 100;
        }

    });

HTML

<div ng-app="Demo" ng-controller="DemoController">
    <div>
        <h4>{{service.name}}</h4>
        <p>Count: {{service.count}}</p>
    </div>
</div>

This JavaScript works as we are passing an object back from the service rather than a value. When a JavaScript object is returned from a service, Angular adds watches to all of its properties.

Also note that I am using 'var self = this' as I need to keep a reference to the original object when the $timeout executes, otherwise 'this' will refer to the window object.

Solution 5

I stumbled upon this question looking for something similar, but I think it deserves a thorough explanation of what's going on, as well as some additional solutions.

When an angular expression such as the one you used is present in the HTML, Angular automatically sets up a $watch for $scope.foo, and will update the HTML whenever $scope.foo changes.

<div ng-controller="FooCtrl">
  <div ng-repeat="item in foo">{{ item }}</div>
</div>

The unsaid issue here is that one of two things are affecting aService.foo such that the changes are undetected. These two possibilities are:

  1. aService.foo is getting set to a new array each time, causing the reference to it to be outdated.
  2. aService.foo is being updated in such a way that a $digest cycle is not triggered on the update.

Problem 1: Outdated References

Considering the first possibility, assuming a $digest is being applied, if aService.foo was always the same array, the automatically set $watch would detect the changes, as shown in the code snippet below.

Solution 1-a: Make sure the array or object is the same object on each update

angular.module('myApp', [])
  .factory('aService', [
    '$interval',
    function($interval) {
      var service = {
        foo: []
      };

      // Create a new array on each update, appending the previous items and 
      // adding one new item each time
      $interval(function() {
        if (service.foo.length < 10) {
          var newArray = []
          Array.prototype.push.apply(newArray, service.foo);
          newArray.push(Math.random());
          service.foo = newArray;
        }
      }, 1000);

      return service;
    }
  ])
  .factory('aService2', [
    '$interval',
    function($interval) {
      var service = {
        foo: []
      };

      // Keep the same array, just add new items on each update
      $interval(function() {
        if (service.foo.length < 10) {
          service.foo.push(Math.random());
        }
      }, 1000);

      return service;
    }
  ])
  .controller('FooCtrl', [
    '$scope',
    'aService',
    'aService2',
    function FooCtrl($scope, aService, aService2) {
      $scope.foo = aService.foo;
      $scope.foo2 = aService2.foo;
    }
  ]);
<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>

<body ng-app="myApp">
  <div ng-controller="FooCtrl">
    <h1>Array changes on each update</h1>
    <div ng-repeat="item in foo">{{ item }}</div>
    <h1>Array is the same on each udpate</h1>
    <div ng-repeat="item in foo2">{{ item }}</div>
  </div>
</body>

</html>

As you can see, the ng-repeat supposedly attached to aService.foo does not update when aService.foo changes, but the ng-repeat attached to aService2.foo does. This is because our reference to aService.foo is outdated, but our reference to aService2.foo is not. We created a reference to the initial array with $scope.foo = aService.foo;, which was then discarded by the service on it's next update, meaning $scope.foo no longer referenced the array we wanted anymore.

However, while there are several ways to make sure the initial reference is kept in tact, sometimes it may be necessary to change the object or array. Or what if the service property references a primitive like a String or Number? In those cases, we cannot simply rely on a reference. So what can we do?

Several of the answers given previously already give some solutions to that problem. However, I am personally in favor of using the simple method suggested by Jin and thetallweeks in the comments:

just reference aService.foo in the html markup

Solution 1-b: Attach the service to the scope, and reference {service}.{property} in the HTML.

Meaning, just do this:

HTML:

<div ng-controller="FooCtrl">
  <div ng-repeat="item in aService.foo">{{ item }}</div>
</div>

JS:

function FooCtrl($scope, aService) {
    $scope.aService = aService;
}

angular.module('myApp', [])
  .factory('aService', [
    '$interval',
    function($interval) {
      var service = {
        foo: []
      };

      // Create a new array on each update, appending the previous items and 
      // adding one new item each time
      $interval(function() {
        if (service.foo.length < 10) {
          var newArray = []
          Array.prototype.push.apply(newArray, service.foo);
          newArray.push(Math.random());
          service.foo = newArray;
        }
      }, 1000);

      return service;
    }
  ])
  .controller('FooCtrl', [
    '$scope',
    'aService',
    function FooCtrl($scope, aService) {
      $scope.aService = aService;
    }
  ]);
<!DOCTYPE html>
<html>

<head>
  <script data-require="[email protected]" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>

<body ng-app="myApp">
  <div ng-controller="FooCtrl">
    <h1>Array changes on each update</h1>
    <div ng-repeat="item in aService.foo">{{ item }}</div>
  </div>
</body>

</html>

That way, the $watch will resolve aService.foo on each $digest, which will get the correctly updated value.

This is kind of what you were trying to do with your workaround, but in a much less round about way. You added an unnecessary $watch in the controller which explicitly puts foo on the $scope whenever it changes. You don't need that extra $watch when you attach aService instead of aService.foo to the $scope, and bind explicitly to aService.foo in the markup.


Now that's all well and good assuming a $digest cycle is being applied. In my examples above, I used Angular's $interval service to update the arrays, which automatically kicks off a $digest loop after each update. But what if the service variables (for whatever reason) aren't getting updated inside the "Angular world". In other words, we dont have a $digest cycle being activated automatically whenever the service property changes?


Problem 2: Missing $digest

Many of the solutions here will solve this issue, but I agree with Code Whisperer:

The reason why we're using a framework like Angular is to not cook up our own observer patterns

Therefore, I would prefer to continue to use the aService.foo reference in the HTML markup as shown in the second example above, and not have to register an additional callback within the Controller.

Solution 2: Use a setter and getter with $rootScope.$apply()

I was surprised no one has yet suggested the use of a setter and getter. This capability was introduced in ECMAScript5, and has thus been around for years now. Of course, that means if, for whatever reason, you need to support really old browsers, then this method will not work, but I feel like getters and setters are vastly underused in JavaScript. In this particular case, they could be quite useful:

factory('aService', [
  '$rootScope',
  function($rootScope) {
    var realFoo = [];

    var service = {
      set foo(a) {
        realFoo = a;
        $rootScope.$apply();
      },
      get foo() {
        return realFoo;
      }
    };
  // ...
}

angular.module('myApp', [])
  .factory('aService', [
    '$rootScope',
    function($rootScope) {
      var realFoo = [];

      var service = {
        set foo(a) {
          realFoo = a;
          $rootScope.$apply();
        },
        get foo() {
          return realFoo;
        }
      };

      // Create a new array on each update, appending the previous items and 
      // adding one new item each time
      setInterval(function() {
        if (service.foo.length < 10) {
          var newArray = [];
          Array.prototype.push.apply(newArray, service.foo);
          newArray.push(Math.random());
          service.foo = newArray;
        }
      }, 1000);

      return service;
    }
  ])
  .controller('FooCtrl', [
    '$scope',
    'aService',
    function FooCtrl($scope, aService) {
      $scope.aService = aService;
    }
  ]);
<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>

<body ng-app="myApp">
  <div ng-controller="FooCtrl">
    <h1>Using a Getter/Setter</h1>
    <div ng-repeat="item in aService.foo">{{ item }}</div>
  </div>
</body>

</html>

Here I added a 'private' variable in the service function: realFoo. This get's updated and retrieved using the get foo() and set foo() functions respectively on the service object.

Note the use of $rootScope.$apply() in the set function. This ensures that Angular will be aware of any changes to service.foo. If you get 'inprog' errors see this useful reference page, or if you use Angular >= 1.3 you can just use $rootScope.$applyAsync().

Also be wary of this if aService.foo is being updated very frequently, since that could significantly impact performance. If performance would be an issue, you could set up an observer pattern similar to the other answers here using the setter.

Share:
221,978
berto
Author by

berto

Updated on August 28, 2020

Comments

  • berto
    berto over 3 years

    I have a service, say:

    factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
      var service = {
        foo: []
      };
    
      return service;
    }]);
    

    And I would like to use foo to control a list that is rendered in HTML:

    <div ng-controller="FooCtrl">
      <div ng-repeat="item in foo">{{ item }}</div>
    </div>
    

    In order for the controller to detect when aService.foo is updated I have cobbled together this pattern where I add aService to the controller's $scope and then use $scope.$watch():

    function FooCtrl($scope, aService) {                                                                                                                              
      $scope.aService = aService;
      $scope.foo = aService.foo;
    
      $scope.$watch('aService.foo', function (newVal, oldVal, scope) {
        if(newVal) { 
          scope.foo = newVal;
        }
      });
    }
    

    This feels long-handed, and I've been repeating it in every controller that uses the service's variables. Is there a better way to accomplish watching shared variables?

  • berto
    berto over 11 years
    Thanks for the response. If the variable in the service is a "primitive" instead of an object, like a string, would that, in turn, require a $watch?
  • Mark Rajcok
    Mark Rajcok over 11 years
    I tried, and I couldn't get $watch to work with a primitive. Instead, I defined a method on the service that would return the primitive value: somePrimitive() = function() { return somePrimitive } And I assigned a $scope property to that method: $scope.somePrimitive = aService.somePrimitive;. Then I used the scope method in the HTML: <span>{{somePrimitive()}}</span>
  • niklr
    niklr over 10 years
    How would you remove the registered callback after the user leaves the page with the FooCtrl?
  • Jamie
    Jamie over 10 years
    @Moo listen for the $destory event on the scope and add an unregister method to aService
  • Jimmy Kane
    Jimmy Kane over 10 years
    @MarkRajcok No don't use primitives. Add them in a object. Primitives are not mutables and so 2way data binding will not work
  • Mark Rajcok
    Mark Rajcok over 10 years
    @JimmyKane, yes, primitives should not be used for 2-way databinding, but I think the question was about watching service variables, not setting up 2-way binding. If you only need to watch a service property/variable, an object is not required -- a primitive can be used.
  • JNissi
    JNissi over 10 years
    Correct me if I'm wrong, but you should $scope.$apply the change in the observer callback in the controller. At least I had to, because all the changes to my service come through events from a flash.
  • XML
    XML about 10 years
    Getting out of the $digest cycle is a Good Thing, especially if the changes you're watching aren't a value that will directly and immediately go into the DOM.
  • osjerick
    osjerick about 10 years
    What are pros of this solution? It needs more code in a service, and somewhat the same amount of code in a controller (since we also need to unregister on $destroy). I could say for execution speed, but in most cases it just won't matter.
  • Jin
    Jin about 10 years
    not sure how this is a better solution than $watch, the questioner was asking for a simple way of sharing the data, it looks even more cumbersome. I would rather use $broadcast than this
  • dtheodor
    dtheodor about 10 years
    $watch vs observer pattern is simply choosing whether to poll or to push, and is basically a matter of performance, so use it when performance matters. I use observer pattern when otherwise I would have to "deep" watch complex objects. I attach whole services to the $scope instead of watching single service values. I avoid angular's $watch like the devil, there is enough of that happening in directives and in native angular data-binding.
  • JerryKur
    JerryKur almost 10 years
    Is there away to avoid .save() method. Seems like overkill when you are just monitoring the update of a single variable in the sharedService. Can we watch the variable from within the sharedService and broadcast when it changes?
  • Adam
    Adam almost 10 years
    I would suggest this solution even when performance is not so much an issue because the behavior is explicitly defined in the code and doesn't depend on library magic. Good answer!
  • Code Whisperer
    Code Whisperer almost 10 years
    The reason why we're using a framework like Angular is to not cook up our own observer patterns.
  • Fabio
    Fabio over 9 years
    What would be the advantage of this method over the $broadcast described bellow by Matt Pileggi?
  • Krym
    Krym over 9 years
    Well both methods have their uses. Advantages of broadcast for me would be human readability and possibility to listen on more places to the same event. I guess the main disadvantage is that broadcast is emiting message to all descendant scopes so it may be a performance issue.
  • Seiyria
    Seiyria over 9 years
    I was having a problem where doing $scope.$watch on a service variable wasn't seeming to work (the scope I was watching on was a modal that inherited from $rootScope) - this worked. Cool trick, thanks for sharing!
  • shrekuu
    shrekuu over 9 years
    Thanks. A little question: why do you return this from that service instead of self?
  • nclu
    nclu over 9 years
    Because mistakes get made sometimes. ;-)
  • Ouwen Huang
    Ouwen Huang over 9 years
    In this set up I am able to change aService values from the scope. But the scope does not change in response the aService changing.
  • Darwin Tech
    Darwin Tech over 9 years
    This also doesn't work for me. Simply assigning $scope.foo = aService.foo does not update the scope variable automatically.
  • Abris
    Abris over 9 years
    How would you clean up after yourself with this approach? Is it possible to remove the registered callback from the promise when the scope is destroyed?
  • Krym
    Krym over 9 years
    Good question. I honestly don't know. I'll try to do some tests on how could you remove notify callback from promise.
  • lostintranslation
    lostintranslation about 9 years
    Correct you cannot assign a scope variable from a Service variable and then $watch('foo' ,... I am using angular 1.3 to test and the watch does not get fired when the service variable changes
  • Cody
    Cody about 9 years
    I don't find this too amazing -- apologies if I seem crude -- but this is a simple getSet approach. Either use an Object.defineProperties(object, ...) or if you're looking for a pubSub methodology, you may as well use $rootScope.$on('changed:someData', setSomeData) & $rootScope.$broadcast('changed:someData', someData). IMAGINARY POINTS OFF for this answer :(
  • Cody
    Cody about 9 years
    Good practice to return this; out of your constructors ;-)
  • Cody
    Cody about 9 years
    I CAN'T BELIEVE THIS ANSWER IS NOT THE TOP-MOST-VOTED!!! This is the most elegant solution (IMO) as it reduces informational-entropy & probably mitigates the need for additional Mediation handlers. I would vote this up more if I could...
  • abettermap
    abettermap about 9 years
    I tried quite a few ways to share data between controllers, but this is the only one that worked. Well played, sir.
  • Zymotik
    Zymotik about 9 years
    Adding all your services to the $rootScope, its benefits and it's potential pitfalls, are detailed somewhat here: stackoverflow.com/questions/14573023/…
  • maxime1992
    maxime1992 about 9 years
    I tried few times before ... I knew there was a better than using scope but i did not succeed when i tried it. This evening i finally did it, so awesome :) !!! You sir deserves more upvotes !
  • JacobF
    JacobF about 9 years
    This is a great approach! Is there a way to bind just a property of a service to the scope instead of the entire service? Just doing $scope.count = service.count doesn't work.
  • superluminary
    superluminary about 9 years
    You need to have a very good use case to justify reimplementing events in Angular.
  • JMK
    JMK almost 9 years
    I prefer this to the other answers, seems less hacky, thanks
  • Charles
    Charles almost 9 years
    This is the correct design pattern only if your consuming controller has multiple possible sources of the data; in other words, if you have a MIMO situation (Multiple Input/Multiple Output). If you're just using a one-to-many pattern, you should be using direct object referencing and letting the Angular framework do the two-way binding for you. Horkyze linked this below, and it's a good explanation of the automatic two-way binding, and it's limitations: stsc3000.github.io/blog/2013/10/26/…
  • Alex Ross
    Alex Ross over 8 years
    You could also nest the property inside of an (arbitrary) object so that it's passed by reference. $scope.data = service.data <p>Count: {{ data.count }}</p>
  • CodeMoose
    CodeMoose over 8 years
    Excellent approach! While there are a lot of strong, functional answers on this page, this is by far a) the easiest to implement, and b) the easiest to understand when reading over the code. This answer should be a lot higher than it currently is.
  • Zymotik
    Zymotik over 8 years
    Thanks @CodeMoose, I've simplified it even further today for those new to AngularJS/JavaScript.
  • Afflatus
    Afflatus over 8 years
    could you please tell me what is the advantage of having the line var self = this; ? why not just use this
  • Zymotik
    Zymotik over 8 years
    Hi @Afflatus, self is being used to maintain a reference to the original this. Please see stackoverflow.com/questions/962033/…
  • magnetronnie
    magnetronnie over 8 years
    You can use setTimeout, or any other non-Angular function, but just don't forget to wrap the code in the callback with $scope.$apply().
  • sarunast
    sarunast over 8 years
    In my opinion you are confusing people with self right here. There is no point using them for creating function because this and self are referring to the same object. It makes sense to use self only inside the function and then again it is not necessary to use it because you can use .bind(this) and when you have a case when you can't use .bind() only then you would use self.
  • Zymotik
    Zymotik over 8 years
    Thank-you for your comments @Stamy. Please can you demonstrate a simpler approach in a jsFiddle? We can then compare and update the example if needed. We do require a reference to the original this when using the $timeout function.
  • sarunast
    sarunast over 8 years
    Here is a link with a code: jsbin.com/guwogo/edit?js,console . I added two approaches, I am fine with either approach. But I would never use self for creating everything. It might be good if you don't understand how this and scopes works but I have never seen anything like this in any decent library.
  • Sarpdoruk Tahmaz
    Sarpdoruk Tahmaz over 8 years
    This is the correct, and easiest solution. As @NanoWizard say, $digest watches for services not for properties that belong to the service itself.
  • ceebreenk
    ceebreenk about 8 years
    @lostintranslation what was your solution for 1.3?
  • Patrik Beck
    Patrik Beck almost 8 years
    Aren't we missing 'return this;' at the end of service definition?
  • Lee Goddard
    Lee Goddard almost 8 years
    Not very Angular, compared to stackoverflow.com/a/20863597/418150
  • jedi
    jedi almost 8 years
    i like the PostmanService but how i must change $watch function on controller if i need to listen more than one variable?
  • hayatbiralem
    hayatbiralem almost 8 years
    Hi jedi, thanks for the heads up! I updated the pen and the answer. I recomment to add another watch function for that. So, I added a new feature to PostmanService. I hope this helps :)
  • hayatbiralem
    hayatbiralem over 7 years
    Actually, yes it is :) If you share more detail of the problem maybe I can help you.
  • Fortuna
    Fortuna over 7 years
    @CodeWhisperer why would we use angulars $broadcast here? Why would I broadcast an event which is probably meant for just a single controller rather than the whole app? And why shouldn't we use a well known pattern if it would fit in the case?
  • NoobSter
    NoobSter over 7 years
    @Zymotik , So, you would still need $watch or $broadcast ,though, when displaying the service data on more than one controller at the same time? jsfiddle.net/qqejytbz/1
  • Zymotik
    Zymotik over 7 years
    @NoobSter no, you would not
  • NoobSter
    NoobSter over 7 years
    @Zymotik , Thanks for the reply. I know this is old. In my fiddle posted above, (forked from your example) the count is not able to update in both controllers, unless I use a $watch. What is the correct way to do this? Fiddle: jsfiddle.net/qqejytbz/1
  • NoobSter
    NoobSter over 7 years
    i moved this to it's own question if you are interested: stackoverflow.com/questions/41786796/…
  • Zymotik
    Zymotik over 7 years
    @noobster please see the first two comments, as I believe this answers your question.
  • João Pereira
    João Pereira about 7 years
    I can't understand the upvotes since this doesn't work. Even if in the service I have an object, its value is not automatically updated in the controller.
  • Norbert Norbertson
    Norbert Norbertson over 6 years
    All this from a "framework" that was supposed to make our lives easier! No wonder they started again! LOL.
  • undefined
    undefined about 6 years
    why it was downvoted.. I have also used similar technique many times and it has worked.
  • Manuel Graf
    Manuel Graf over 5 years
    $watch and $rootScope.$emit are both quite expensive as they have to traverse the whole scope, right? As I am working a lot with really cpu expensive frontend, this matters to me and I also already used it in 1.4. Observer pattern still is a direct connection and nothing has to be traversed, which for high n it might be faster. Especially if there already a lot of rootScope listeners? I didnt benchmark but thats how i understand it. if you have other information, please enlighten me!
  • Mrl0330
    Mrl0330 almost 5 years
    @CodeWhisperer 's upvoted response shouldn't be so upvoted. While true, it completely ignores the rationale of creating your own observer pattern in a service, i.e. performance. If you've ever used watchers in an angularjs application you know how quickly they can degrade performance. A lighter weight approach like this surely has some applicability.
  • Amna
    Amna almost 5 years
    May God bless you. I have wasted like million hours I would say. Because I was struggling with 1.5 and angularjs shifting from 1 to 2 and also wanted to share data
  • Oguzhan
    Oguzhan over 3 years
    consider $watch works with the methodology of long polling where as observer pattern calls method(s) only once (with right garbage collecting implementation) when job is done. I think observer pattern leads far beyond in performance-wise in my opinion.