1

I'm trying to use a service to share data between two controllers. But I'm not seeing the data update after changing the data with XHR.

angular    
  .module('appy', [])
  .service('MyService', function($http){
    return {
       dataToChange: {
       },
       ajaxy: function(url){
         var self = this;
         setTimeout(function(){
           console.log('now we are in the service!');
           self.dataToChange = 'something new';
           console.log(this.dataToChange);
         }, 1000);
       }
    };
  })
  .controller('c1', ['$scope', 'MyService', function($scope, MyService){
    $scope.myService = MyService;
    $scope.ajaxy = function(){
      $scope.myService.ajaxy();
    };
  }])
  .directive('dirOne', function(){
    return {
      restrict: 'E',
      templateUrl: 'dirone-temp.html'
    };
  })
  .controller('c2', ['$scope', 'MyService', function($scope, MyService){

      $scope.myData = MyService.dataToChange;  

  }])
  .directive('dirTwo', function(){
    return {
      restrict: 'E',
      templateUrl: 'dirtwo-temp.html'
    };
  });

But nothing happens when ajaxy makes his update. I tried setting this up like recommended by this question. No joy. I also tried adding a watch.

$scope.$watch(function(){
  return MyService.ajaxy();
}, function(oldVal, newVal){
  $scope.somethingToChange = newVal;
  console.log($scope.somethingToChange);
});

But nothing happens still. Also this object is pretty big. I would prefer not to $watch it.

JSBIN

Cœur
  • 37,241
  • 25
  • 195
  • 267
1252748
  • 14,597
  • 32
  • 109
  • 229
  • 2
    First off just fyi, in the `setTimeout`, your `this` is referring to `window`. You need to bind the `setTimeout` callback to your object (however, this won't fix your main problem). – Josh Beam Mar 23 '16 at 23:15
  • 1
    Great point. Didn't even think of that when I was mocking this up. Thanks! – 1252748 Mar 23 '16 at 23:15
  • I would suggest using a root controller so that you can get the data in that controller and share it between c1 and c2. – Swimburger Mar 23 '16 at 23:28
  • @sniels Why do you think that that is the best option? How would you go about setting up a "root controller"? Thanks. – 1252748 Mar 23 '16 at 23:32
  • I'm not saying it's the best suggestion, but it's what worked for me in the past. I'll try to put together an example. – Swimburger Mar 23 '16 at 23:34
  • @sniels Cool. Thanks! – 1252748 Mar 23 '16 at 23:54

1 Answers1

1

When you want to share your data like you are trying to do, you have to share an object (like the so example you gave) and never replace that object. If you do replace the object in the service, the $scope will still reference to the old objects, which is the problem in your case.

  1. You set MyService.dataChange to an empty object.
  2. You inject that object into the scope of your controllers
  3. When you call ajaxy(), you change MyService.dataChange to the string 'something new'. At this point, the scope still holds the reference to the old object and not to the string.

To solve this, instead of replacing MyService.dataChange you need to add a property to that object. When you bind to that property of the shared object, you will notice that you have to run ajaxy() twice. To solve this, instead of using timeout, you need to use the angular $timeout.

angular    
  .module('appy', [])
  .service('MyService', function($http, $timeout){
    return {
       // never replace this object, put the values you want to share as a property inside.
       sharedObject: {
         name: 'old value'
       },
       ajaxy: function(url){
         var self = this;
         // use the angular version of $timeout to make sure your scope is in sync
         $timeout(function(){
           console.log('now we are in the service!');
           self.sharedObject.name = 'new value';
           console.log(self.sharedObject);
         }, 1000);
       }
    };
  })
  .controller('c1', ['$scope', 'MyService', function($scope, MyService){
    $scope.myData = MyService.sharedObject;
    $scope.ajaxy = function(){
      MyService.ajaxy();
    };
  }])
  .directive('dirOne', function(){
    return {
      restrict: 'E',
      templateUrl: 'dirone-temp.html'
    };
  })
  .controller('c2', ['$scope', 'MyService', function($scope, MyService){ 
    $scope.myData = MyService.sharedObject;
  }])
  .directive('dirTwo', function(){
    return {
      restrict: 'E',
      templateUrl: 'dirtwo-temp.html'
    };
  });
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body ng-app="appy">
  
  <dir-one ng-controller="c1"></dir-one>
  <hr>
  <dir-two ng-controller="c2"></dir-two>  
  
  <script id="dirone-temp.html" type="text/ng-template">
    template one
    <button ng-click="ajaxy()">something time consuming</button>
    <div>{{myData.name}}</div>
  </script>
  
  <script id="dirtwo-temp.html" type="text/ng-template">
    template two
    <div>{{myData.name}}</div>
  </script>
</body>
</html>
Swimburger
  • 6,681
  • 6
  • 36
  • 63
  • @thomas, I didn't go with the rootcontroller and shared scope since this strategy does seem cleaner. – Swimburger Mar 24 '16 at 00:12
  • Thank you! Sadly it is late where I am and I will have to check this out in the morning. How would this change if `ajaxy` were an actual `$http()` `get`? – 1252748 Mar 24 '16 at 00:24
  • Just replace $timeout with `$http.get(url).then( function success(response) { self.sharedObject.data=response.data;}, function error(response){ ...} );` – Swimburger Mar 24 '16 at 00:26
  • @thomas don't forget to accept the answer in the morning if it works for you. – Swimburger Mar 24 '16 at 00:28
  • Hmm. I'll have to see about just replacing `$timeout` with `$http`. I think that's exactly where I started in the first place! Thanks. – 1252748 Mar 24 '16 at 00:29