0

I am working with controllers and services in angular. It was my understanding that in the scenario of sharing date through a service between controllers that it is my responsibility to update the service since it doesn't have scope. I set a watch on the input fields and updated the service when the input changed. Why is the service in the other controller not getting updated? It is a singleton and has one instance..shouldn't the reference in the other controller be updated? The only solution I have found is to add a watch to listen for changes on the service and model but now I have two watch functions per controller to listen both ways. Is this correct? I feel that if I update the service the value should get updated in the other controller because everything is pointing to the same value.

https://jsfiddle.net/86hry8a5/

  <div ng-controller="mainController as mainCtrl">
    <div>
      <input type="text" ng-model="mainCtrl.message" />
    </div>
    <div>
      {{mainCtrl.message}}
    </div>
  </div>
  <hr/>
  <div ng-controller="secondController as secondCtrl">
    <div>
      <input type="text" ng-model="secondCtrl.message" />
    </div>
    <div>
      {{secondCtrl.message}}
    </div>
  </div>
  .controller('mainController', function(mainService, $scope){
      var self = this;
      self.message = mainService.message;
      $scope.$watch(function(){return mainService.message}, function(){
          self.message = mainService.message;
      });
      $scope.$watch(function(){return self.message}, function(){
          mainService.message = self.message;
      });
  })

  .controller('secondController', function(mainService, $scope){
      var self = this;
      self.message = mainService.message;
      $scope.$watch(function(){return mainService.message}, function(){
          self.message = mainService.message;
      });
      $scope.$watch(function(){return self.message}, function(){
          mainService.message = self.message;
      });
  })

  .service('mainService', function(){
      var self = this;
      self.message = 'service';

  }
);
georgeawg
  • 48,608
  • 13
  • 72
  • 95
CW1
  • 331
  • 1
  • 4
  • 14
  • Your fiddle is working fine! Both inputs change when one input is changed. – Pop-A-Stash Jun 01 '17 at 16:31
  • 1
    He did mention that his fiddle works fine. He was looking for a better way to do this. Using events is a different way to do what he is already doing. – CodeWarrior Jun 01 '17 at 16:32

2 Answers2

1

Updating the value in the service will not automatically update the value in the second controller since the second controller is not 'watching' for changes to the service's value.

In order to achieve what you are looking for, you will have to create your own 'watch' just like you have already done.

But, a better way to do this would be to emit events. Each controller will emit an event when it updates the value in the service. The other controller will listen to this event and update it's local copy.

use ng-change on the input box like this:

  <input type="text" ng-model="mainCtrl.message" ng-change="mainCtrl.changeHandler()" />

Controller code in both controllers that emits and subscribes to events:

      self.changeHandler = function () {
        mainService.message = self.message;
        $rootScope.$broadcast('MAIN_SERVICE_VALUE_CHANGED');
      };
      $rootScope.$on('MAIN_SERVICE_VALUE_CHANGED', function () {
        self.message = mainService.message;
      });

Here is a working fork of your fiddle updated with event logic

CodeWarrior
  • 2,721
  • 3
  • 18
  • 18
  • 1
    He was looking for a better way to do this. Using events is a different way to do what he is already doing. It's a matter of personal preference which option he decides to use. – CodeWarrior Jun 01 '17 at 16:34
  • I am a "if it's not broken don't fix it" type of person. Unless there is a very good, specific reason to change something, I won't change it. Can you tell us what benefit is received from using events and `$on` over scope `$watch` in the controllers? – Pop-A-Stash Jun 01 '17 at 16:39
  • Other than that clarification, good answer! – Pop-A-Stash Jun 01 '17 at 16:45
  • 1
    Personally, I prefer events. I do not usually hard code event names like I've done in my example. I usually use an event bus service, where I keep all of the events used by my application. This allows me to come to one place in my application where all of my events are located. From here, it is easy for me to find out who all are emitting and subscribing to these events. This is extremely useful when doing performance tuning for large applications. Again, like I said, it comes down to personal preference! :-) – CodeWarrior Jun 01 '17 at 16:47
  • Thanks for the answer this would be a cleaner way to do it. I still don't understand why the value in the second controller would not get updated if its referencing the value in the service though. In older tutorials Ive seen this done.. check out Tony Alicea Learn and Understand AngularJS. In his tutorial he does it with only the one watch for the model and since the Service has one instance and holds that value it gets automatically updated in the second Controller. Im guessing this is a version difference with angular? – CW1 Jun 01 '17 at 16:59
  • Can you give an example of your event bus service? I am working with events right now and currently I am thinking of moving them to .constant and just injecting that in and referencing that way. How would your event bus work? – CW1 Jun 01 '17 at 17:02
  • I see your confusion. In fact what you are looking for will work when you store an object reference with a property rather than the property itself in your controller. Check out this example: https://jsfiddle.net/CodeWarrior404/5framtzy/4/ Here, instead of storing the property itself in the controller, I'm storing a reference to an object that has that property. In this case, if you update the value of the property, everyone who has a reference to that, including the service and other controllers will get the updated value. – CodeWarrior Jun 01 '17 at 17:15
  • thank you! this is what i was looking for! Why does this only work with reference to an object? and not just a property in the service? – CW1 Jun 01 '17 at 17:25
0

Why does this only work with reference to an object? and not just a property in the service?

References are entities that point to an object in memory. When a function returns an object reference, each controller gets the same pointer.

enter image description here

The contents of the object is shared by all the controllers. Changes to the contents is seen by all the controllers.

With primitives only the value is transfered. Subsequent changes are not seen by all the controllers.

See also, JavaScript Pass Object as a Reference

georgeawg
  • 48,608
  • 13
  • 72
  • 95