2

I'm new to AngularJS, so this might actually point to some core concept that I don't yet understand completely. I'm trying to treat "remote data" like local data using $q & promise objects. Till the time the remote data is not fetched, the promise object doesn't resolve, but as soon as it resolves, I want all the dependent data-bindings in the view to get updated. However, the following approach is resulting in an infinite loop where remote_total is being called repeatedly, even through the previous call to remote_total resulted in a resolved promise object.

Here's what my view looks like

<div ng-controller="MyController">
  {{ remote_total() }}
</div>

Here's the relevant snippet from the controller:

function MyController($scope, $q, $http) {
  $scope.remote_total = function() {
    var def = $q.defer();
    $http.get('/my/remote/service.json').success(function(data) {
      def.resolve(data);
    });
    return def.promise;
  }
}

Firstly, it would be great if anyone could explain to me why exactly is this getting into an infinite loop. Secondly, what's the best way to achieve what I'm trying to do?

2 Answers 2

5

NOTE: See UPDATE below for Angular 1.2+

Actually interesting thing with AngularJS's promises (that $q provides) that they're recognized by AngularJS. And another that they are chainable!

So in your code you can just do

$scope.remote_total = $http.get('/my/remote/service.json').then(function(data) {
  return data;
});

and at view just <div>{{remote total}}</div> (note: not function, just value).

And AngularJS will automatically recognize it's promise, resolve $http.get promise, then chain into your function and put resulting value into template.

That's all :)

UPDATE: Automatic de-wrapping of promises are disabled by default in AngularJS 1.2 and would be completely removed in 1.3.

Code that will work:

$http.get('/my/remote/service.json').then(function(response) {
  $scope.remote_total = response.data;
});
Sign up to request clarification or add additional context in comments.

4 Comments

How come I have to say {{ remote_total }} and not {{ remote_total() }}. Is this simply angular magic or is there an actual difference in what both of them mean?
Angular detects, that it is a promise and resolve it before showing
I think just $scope.remote_total = $http.get('/my/remote/service.json') will suffice.
This will no longer work in 1.2 without a flag, and will not work in 1.3 ever. Promises are no longer automatically unwrapped.
3

Angular does dirty checking to achieve 2 way binding. In each cycle, it stores the previous value of property being watched and compares it with the new value. If that property is a function, it is called and the result is being compared.

You did put a function to be watched and in each cycle, your function is being called, hence resulting in http request.

What you want to do is to create the promise on its own and attach that promise to scope for watching (or showing in view).

Also, $http service already returns a promise, you don't need to create another promise.

function MyController($scope, $q, $http) {
  $scope.getRemoteTotal = function() {
    var def = $http.get('/my/remote/service.json').then(function(response) {
      $scope.remoteTotal = response.data;
    });
  }
  $scope.getRemoteTotal();
}

.

<div ng-controller="MyController">
  {{ remoteTotal }}
</div>

3 Comments

So, if I understand this correctly, {{ remote_total }} sets up a watch on the function, which would mean that angular would call that function each digest-cycle (?) to figure out if it has changed or not. And each time it's called, it would end up making the HTTP request. Correct?
yes, if the item being watched is a function, it is called in each cycle. here misko's (one of the core developers) explanation on angular's databinding stackoverflow.com/questions/9682092/databinding-in-angularjs
this is very esoteric - even unintuitive - behaviour from angular

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.