18

I have created a service to isolate the business logic and am injecting it in to the controllers that need the information. What I want to ultimately do is have the controllers be able to watch the values in the service so that I don't have to do broadcast/on notification or a complex message passing solution to have all controllers be notified of changes to the data in the service.

I've created a plnkr demonstrating the basic idea of what I'm trying to do.

http://plnkr.co/edit/oL6AhHq2BBeGCLhAHX0K?p=preview

Is it possible to have a controller watch the values of a service?

nathasm
  • 2,524
  • 5
  • 24
  • 35
  • 2
    My downvote is because the plunker is gone, so is the code, which renders this question (and answer) useless. :( Put the code here and I'll +1 your question. – Bernhard Hofmann Jun 12 '14 at 10:09

1 Answers1

49

You were already doing it right. i.e pushing the service into a scope variable and then observing the service as part of the scope variable.

Here is the working solution for you :

http://plnkr.co/edit/SgA0ztPVPxTkA0wfS1HU?p=preview

HTML

<!doctype html>
<html ng-app="plunker" >
<head>
  <meta charset="utf-8">
  <title>AngularJS Plunker</title>
  <script>document.write('<base href="' + document.location + '" />');</script>
  <link rel="stylesheet" href="style.css">
  <script src="http://code.angularjs.org/1.1.3/angular.js"></script>
  <script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
  <button ng-click="start()">Start Count</button>
  <button ng-click="stop()">Stop Count</button>
  ControllerData: {{controllerData}}
</body>
</html>

Javascript :

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

app.service('myService', function($rootScope) {
  var data = 0;
  var id = 0;

  var increment = function() {
    data = data + 1;
    $rootScope.$apply();
    console.log("Incrementing data", data);
  };

  this.start = function() {
    id = setInterval(increment, 500) ;

  };

  this.stop = function() {
    clearInterval(id);
  };

  this.getData = function() { return data; };

}).controller('MainCtrl', function($scope, myService) {
  $scope.service = myService;
  $scope.controllerData = 0;

  $scope.start = function() {
    myService.start();
  };

  $scope.stop = function() {
    myService.stop();
  };

  $scope.$watch('service.getData()', function(newVal) {

    console.log("New Data", newVal);
    $scope.controllerData = newVal;
  });
});

Here are some of the things you missed :

  1. The order of variables in $scope.$watch were wrong. Its (newVal,oldVal) and not the other way around.
  2. Since you were working with setInterval , which is an async operation you will have to let angular know that things changed. That is why you need the $rootScope.$apply.
  3. You cant $watch a function, but you can watch what the function return's.
ganaraj
  • 26,841
  • 6
  • 63
  • 59
  • 1
    The punker code is gone (or I can't see it). Please put the contents of the answer here, not on a 3rd party website to get your +1. – Bernhard Hofmann Jun 12 '14 at 10:10
  • @BernhardHofmann++. I haven't clicked on a working plnkr in ages. – Charlie Schliesser Jul 07 '14 at 22:23
  • 2
    Note a note you can use angular's $interval and it would remove the need for $rootScope.$apply() – pwhe23 Apr 06 '15 at 18:25
  • I'm using requestAnimationFrame and $rootScope.$apply(); fixed it for me. Thanks! – Mark Robson Oct 22 '15 at 19:06
  • I really don't like that. So you will watch everywhere every variables like this. That answer to the question is good, but it s a bad approach. I think he should put data and id in a single object. If the controller call a method from the service, just return the single object on each setMethod, so no watch and no emit. If a directive change the single object and we need to notified the controller, just fire event with broadcast from service to controller, i don't know why you want, it's not a "complex message". But stop watching everything !!! – amdev Aug 01 '16 at 09:02