Ciel
Ciel

Reputation: 4440

AngularJS - run code when all other controllers and such are done

I am new to Angular, and am attempting to do some page blocking while content loads. This content includes ajax requests and some other things, all wrapped into controllers.

Previously, I was handling this with a simple queue system using $.Deferred in jQuery, but since the way the program "flows" in Angular is fundamentally ... not like that, I'm at a bit of a loss. I'm having a hard time thinking of a way to "tell" my program/module/app that "Yes. I am done loading. You are finished now".

Has anyone else experienced this and discovered a solution to it?

My first thought was to just call events at the end of each controller, but that has yielded some mixed results. There has to be a more professional, reliable solution.

Upvotes: 1

Views: 106

Answers (2)

Matt Bucci
Matt Bucci

Reputation: 2140

One way to do this might be to create a Service and inject it into each of the controllers, create a function called register which takes a promise pushes it into an array within the service. You could then listen for when all promises are resolved, and load the page.

I would imagine you would need some sort of basic timer promise as well to solve the edge case at the beginning of page load, when the controllers have yet to register their promises.

Within the controllers you would register your promise for the entire controller, or for individual functions, that is up to you, I would create 1 promise per controller with var deferred = $q.defer() Scheduler.register(deferred.promise), then resolve deferred when all other promises had been resolved.

Here's a simple example: http://jsfiddle.net/4mf1pkLj/62/

$window.ready() is called when all promises are resolved within scheduler

 .controller('OtherCtrl', ['$scope', '$q', 'scheduler',
     function($scope, $q, scheduler) {
         var differed = $q.defer()
         $scope.state = "waiting"
         //do some blocking ajaxy thing
         setTimeout(function() {
             differed.resolve()
             $scope.state = "ready"
         }, 5000);
         scheduler.register(differed.promise)
     }
 ])
 .service('scheduler', ['$http', '$q', '$window',
     function($http, $q, $window) {
         var promises = [];
         var finished = $q.defer().promise
         this.register = function(promise) {
             promises.push(promise)
             finished = wait()
          }

         function wait() {
             return $q.all(promises) 
         }

         function listen() {
             finished.then(function() {
                 //render your content
                 $window.ready() 
             })
         }

         //wait until everything is registered
         setTimeout(function(){listen()},200)
     }

Upvotes: 1

apairet
apairet

Reputation: 3172

ngRouter module permits to just do that with the resolve property. It will delay view rendering till all data is loadedThe complete doc can be found here: https://docs.angularjs.org/api/ngRoute

A basic example (from the doc):

angular.module('yourApp', ['ngRoute']);

angular.module('yourApp')
    .config(function($routeProvider) {
        $routeProvider
            .when('/Book/:bookId', {
                templateUrl: 'book.html',
                controller: 'BookController',
                resolve: {
                    // I will cause a 1 second delay
                    delay: function ($q, $timeout) {
                        var delay = $q.defer();
                        $timeout(delay.resolve, 1000);
                        return delay.promise;
                    }
                }
            })
            .when('/Book/:bookId/ch/:chapterId', {
                templateUrl: 'chapter.html',
                controller: 'ChapterController'
            });
    });

Your view will not be rendered before the promise delay is resolved.

Upvotes: 1

Related Questions