0

I am trying to run the following code before any of my AngularJS app controllers, directives run, but unfortunately the app main page controller loads before this code finish executing, so I was wondering if there is a way to ensure that all my app controllers, directives won't run / load before this code finish completely? Thanks

  myApp.run(['TokenSvc',function (TokenSvc) {

        TokenSvc.getToken().then(function(serverToken){
            console.log('Got it...');
        }, function(status){
            console.log(status);
        });    
  }]);
2
  • Since you're waiting for an asynchronous operation that could take a while to finish, does it make sense to notify (inside the success callback and through $rootScope.$emit, for example) your main page controller to proceed whatever it needs to do with this server token? Commented Apr 27, 2014 at 13:10
  • @spacemigas I thought about using $rootScope.$broadcast but then I have to wrap up all of the controller code inside the event $scope.$on('event'.... but I was wondering if there is an alternative solution where I don't need to add event dependent code in the app controllers Commented Apr 27, 2014 at 13:42

2 Answers 2

2

Most commonly you'll see resolve in the ng-route or ui-router $state definition used for this concern, but that can be problematic. If the resolution takes a while, the user will just be staring at a blank screen. Of course, you can mitigate this problem by using an interceptor to display a loader, but I'd argue that that's outside the intended utility of interceptors.

I like to use something to manage the initialization promise(s), and inject that thing into top-level Controllers (i.e. either a Mediator or Observer pattern):

(function () {
    function UserInfoLoader($q, facebookService, githubService) {
        var _initPromise = null;

        function initialization() {
            var deferred = $q.defer(),
                _initPromise = deferred.promise,
                facebookLoading = facebookService.somePromiseFunc(),
                githubLoading = githubService.somePromiseFunc();

            $q.all([facebookLoading, githubLoading])
                .then(function (results) {
                    // do something interesting with the results
                    deferred.resolve();
                    // set the promise back to null in case we need to call it again next time
                    _initPromise = null;
                });

            return promise;
        }

        this.initialize() {
            // if there's already an initialization promise, return that
            return _initPromise ? _initPromise : initialization();
        }
    }

    angular.module('myApp').service('userInfoLoader', UserInfoLoader);
}());

This is great, because you can have multiple Controllers depend on the same workflow logic and they'll only produce one promise.

(function () {
    function UserProfileController($scope, userInfoLoader) {
        $scope.loading = true;

        function load() {
            userInfoLoader.initialize().then(function () {
                $scope.loading = false;
            });
        }

        load();
    }

    function UserMessagesController($scope, userInfoLoader) {
        // same sort of loading thing
    }

    angular.module('myApp')
        .controller('userProfileController', UserProfileController)
        .controller('userMessagesController', UserMessagesController)
    ;
}());

To borrow from Mr. Osmani's book linked above, the loader service is like an air traffic controller. It coordinates the schedules of and passing information between multiple "airplanes", but they never have to talk to each other.

Another approach that I've seen is to use a FrontController, usually added on the body element, that manages a global loader, showing it during long-running async operations. That one's pretty simple, so I won't write it all out.

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

1 Comment

This is an excelent answer. Unfortunately the question is a bit unclear, so I encountered this only by coincident.
1

Do the fowllowing in each route:

$routeProvider.when("/your/path", {
    templateUrl: "template/path",
    controller: "controllerName",
    resolve: {
            getToken: ['TokenSvc',function (TokenSvc) {
                    return TokenSvc.getToken();
            }]
    }
});

You need that the getToken method return always the same object. Something like this:

obj.token = null;
obj.getToken = function(){
    if(!obj.token){
        var deferred = $q.defer();
        obj.token = deferred;
        deferred.promise.then(function(serverToken){
            console.log("Got it. The token is ",serverToken);
        }, function(status){
            console.log("something is wrong ", status);
        });
        $http.get("url/to/token")
        .success(function(data){
            deferred.resolve(data);
        })
        .error(function(data, status){
            deferred.reject(status);
        });
    }

    return obj.token.promise;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.