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

2
  • 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. Commented Aug 18, 2015 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 stackoverflow.com/a/23382400/373655 . I'm still not entirely sure why this is happening though Commented Aug 18, 2015 at 17:22

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

3 Comments

@DavidL not sure what you mean? The UI should still be responsive -- it will just not show the results until the operation is done
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.
Ah, gotcha -- I started to think that was what you meant as I was typing my response, but figured I'd post anyways.
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);
});

1 Comment

Thanks, I added a $timeout in controller to execute $digest, but still not working. please check the codepen
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.