15

NOTE: the fiddle uses an old version of Angular, and that it's not working any more because as of 1.2 the Angular template engine does not handle promises transparently.

I'm looking into chaining promises to populate my scope, and then having the scope automatically update the dom.

I'm running into problems with this though.. If I call "then" on an already resolved promise, it creates a new promise (that will call the success function asynchronously but almost immediately). I think the problem is that we've already left the digest cycle by the time the success function is called, so the dom never updates.

Here is the code:

<div ng-controller="MyCtrl">
    Hello, {{name}}! <br/>
    {{name2}}<br/>
    <button ng-click="go()">Clickme</button><br/>
    {{name3}}
</div>

var myApp = angular.module('myApp',[]);

function MyCtrl($scope, $q) {
    var data = $q.defer();    
    setTimeout(function() {$scope.$apply(data.resolve("Some Data"))}, 2000);
    var p = data.promise;

    $scope.name = p.then(angular.uppercase);
    $scope.name2 = p.then(function(x) { return "Hi "+x;});
    $scope.go = function() {
            $scope.name3 = p.then(function(x) { 
                // uncomment this to make it work:
                //$scope.$apply();
                return "Finally: "+x;
            });
    };
 }

http://jsfiddle.net/QZM4d/

Is there some way to make this work without calling $apply every time I chain promises?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Karen Zilles
  • 7,633
  • 3
  • 34
  • 33
  • Why are you putting a promise in the `go()` function? – Plynx Feb 02 '13 at 01:38
  • There is a promise there because I'm running code on the result of the original "p" promise. I want the code to run correctly whether or not "p" has been resolved... hence we create a new promise from the old promise. – Karen Zilles Feb 04 '13 at 19:48

1 Answers1

15

NOTE: the fiddle uses an old version of Angular, and that it's not working any more because as of 1.2 the Angular template engine does not handle promises transparently.

To quote @pkozlowski.opensource:

In AngularJS the results of promise resolution are propagated asynchronously, inside a $digest cycle. So, callbacks registered with then() will only be called upon entering a $digest cycle.

So, when the button is clicked, we are in a digest cycle. then() creates a new promise, but the results of that then() will not be propagated until the next digest cycle, which never comes (because there is no $timeout, or $http, or DOM event to trigger one). If you add another button with ng-click that does nothing, then click that, it will cause a digest cycle and you'll see the results:

<button ng-click="">Force digest by clicking me</button><br/>

Here's a fiddle that does that.

The fiddle also uses $timeout instead of setTimeout -- then $apply() isn't needed.

Hopefully it is clear when you need to use $apply. Sometimes you do need to call it manually.

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • 1
    Okay, I think I've got my head around it now. I ended up rewriting my question and code so many times that I got about 80% of the way there myself. It kind of seems too bad that we have to manually force digest for resolving promises, but it could still be interesting to code that way. – Karen Zilles Feb 04 '13 at 19:57
  • I believe you can use $http or $q promises to allow an angular digest cycle to happen after the calls. I'm in search of the best way right now. – Oak May 02 '14 at 18:40
  • 2
    NOTE that the fiddle uses an old version of Angular, and that it's not working any more because as of 1.2 the Angular template engine does not handle promises transparently. – user276648 Nov 27 '14 at 02:22
  • Adding a button that does nothing would work, but why clicking ng-click="go" twice doesn't trigger a digest cycle? – axings Nov 29 '16 at 03:51
  • @axings, what is `go`? If it is not a valid Angular expression, then a digest cycle probably won't run. – Mark Rajcok Nov 29 '16 at 18:28
  • Sorry @MarkRajcok I meant ng-click="go()", according to this answer clicking a button would trigger a digest cycle, then why clicking
    would indeed do so but clicking
    a second time does not?
    – axings Dec 06 '16 at 04:42
  • >> kind of seems too bad that we have to manually force digest for resolving promises. Agree. – SamFlushing Mar 10 '17 at 12:51