3

I want share data between controller, so though service, I can easily get and set data. But if a data set by a function in function in service, it's can not update controller's data. like this:

_this.testf = function (){
  _this.test.push(1);
  _this.test.push(1);
  _this.test.push(1);

  $timeout(function (){
    _this.test.push(2);//this can not seen in controller
  }, 1000);
}

The new data still can be update by $watch, but it's not same as reference.

Here is the example code:http://codepen.io/nsbp/pen/MwdqRq

Tom
  • 7,640
  • 1
  • 23
  • 47
nbsp
  • 161
  • 2
  • 8
  • please check the codepan link, I think it's shoud to be 1,1,1,2, but no 2 at last if no $watch in controller. – nbsp Aug 18 '15 at 16:39
  • One odd thing I noticed is that if you replace your `ng-bind` with {{}} it will work. This answer sheds some light on why that might be http://stackoverflow.com/a/23382400/373655 . I'm still not entirely sure why this is happening though – rob Aug 18 '15 at 17:22

3 Answers3

1

This is because the $timeout service is asynchronous.

You could have your service return the value and then in your controller set it in the returned promise, like:

.service('List', function($timeout) {
  var _this = this;

  _this.test = [];
  _this.testf = function (){
    _this.test.push(1);
    _this.test.push(1);
    _this.test.push(1);
    return $timeout(function (){
      _this.test.push(2);
      return _this.test;
    }, 1000);
  }
})
.controller('MainCtrl', function($scope, $timeout, List) {
  List.testf().then(function(test) {
    $scope.test = test;
  });
});

Here is an updated codepen.

Tom
  • 7,640
  • 1
  • 23
  • 47
  • @DavidL not sure what you mean? The UI should still be responsive -- it will just not show the results until the operation is done – Tom Aug 18 '15 at 17:11
  • sorry, I didn't mean the entire UI, but rather that the data will not resolve until the entire asynchronous call resolves, whereas the OP might want to immediately return the set of data, then update once additional data filters in. – David L Aug 18 '15 at 17:14
  • Ah, gotcha -- I started to think that was what you meant as I was typing my response, but figured I'd post anyways. – Tom Aug 18 '15 at 17:15
1

The issue occurs because your controller's digest cycle has already ended by the time the timeout period elapses. If you add a quick and dirty check like the following to your pen:

_this.testf = function (){
    _this.test.push(1);
    _this.test.push(1);
    _this.test.push(1);
    $timeout(function (){
      _this.test.push(2);
      alert(_this.test.length);
    }, 1000);
}

You will see that the array IS updated.

As a result, you can either use $watch or $timeout in your controller, or broadcast an event from your directive to your controller to trigger a new digest cycle.

If using $timeout, your assigned binding won't recognize that the data has updated (whereas $watch will if using a deep watch, watching the array length, or if you're using $watchCollection), but you can force it to update by copying the array, which is actually creating an entirely new reference.

.controller('MainCtrl', function($scope, $timeout, List) {
    $scope.test = List.test = [];
    List.testf();
    $timeout(function (){
        $scope.test = angular.copy(List.test); 
    }, 4000);
});
David L
  • 32,885
  • 8
  • 62
  • 93
  • Thanks, I added a $timeout in controller to execute $digest, but still not working. please check the codepen – nbsp Aug 18 '15 at 17:00
1

The problem is that ng-bind will only update when the object you pass into it is replaced with another object. Since you are passing it an array and you are only updating the element of the array ng-bind will not update. You could fix this by either using an ng-repeat or by using {{}} in place of ng-bind.

  <p>
    <span ng-repeat="item in test track by $index" ng-bind="item"></span>
  </p>

http://codepen.io/anon/pen/QbRZxz

rob
  • 17,995
  • 12
  • 69
  • 94