Search code examples
javascriptscopeangularjsdigestdeferred

How can I get a deferred object set in one scope to be included in the digest of another scope in AngularJS?


Folks, I have a problem. I'm using AngularJS and I am setting up a deferred object inside an Angular service definition:

angular.module('myServices', []).
  service('Brand', function($rootScope, $q){
    var service = {
        getNext: function() {
            var deferred = $q.defer();

            setTimeout(function() {
                deferred.resolve('foo');
            }, 2000);

            return deferred.promise;
        }
    };

    return service;
});

The service is used in my controller:

angular.module({
    controllers: {
        brand: function($scope, Brand) {
            $scope.changeBrand = function() {
                $scope.brand = Brand.getNext();
            }
        }
    }
}, ['myServices]);

And finally the view waits for the promise to be resolved and then displays it:

<a ng-click="changeBrand()" id="changeBrand">Change</a>
<p ng-bind="brand"></p>

The trouble is that although the promise is being resolved and although the view does just fine waiting for the promise to be resolved and showing the result, it doesn't do it immediately. It only shows up when I add this code and click the link:

// View:
<a ng-click="apply()">Apply</a> 

// Controller:
$scope.apply = function() {
    $scope.$apply();
};

Does the part of the digest that the promise lives in exist separately from the digest that runs when the view/controller's scope is changed? How can I get the digest to run on the view/controller's scope automatically when the deferred resolves?

Thanks!


Solution

  • Few days ago I had the same issue while implementing lazy controllers with promisses. When you take a look at the documentation about routeProvider there is a sample that uses $timeout service with promises (https://github.com/angular/angular.js/blob/master/src/ng/route.js#L186). $timeout implemation uses $rootScope.$apply() to call the lifecycle and refresh the bindings (AFAIK it calls all dirty check functions - it's how bindings work in Angular). So the solution to my problem (and I think also yours) was adding $rootScope.$apply() after the resolve(), like this:

     setTimeout(function() {
                deferred.resolve('foo');
                $rootScope.$apply();
            }, 2000);