21

My problem is that i need a service loaded before the controller get called and the template get rendered. http://jsfiddle.net/g75XQ/2/

Html:

<div ng-app="app" ng-controller="root">
    <h3>Do not render this before user has loaded</h3>            
    {{user}}
</div>
​

JavaScript:

angular.module('app', []).
factory('user',function($timeout,$q){
    var user = {};            
    $timeout(function(){//Simulate a request
        user.name = "Jossi";
    },1000);
    return user;
}).
controller('root',function($scope,user){

    alert("Do not alert before user has loaded");
    $scope.user = user;

});
​

1
  • I think it would be a lot easier if you just handle an unloaded state in the controller. Trying to get control over controller lifecycle seems to defeat the simplicity of angular model. Commented Sep 30, 2012 at 16:10

6 Answers 6

14

You can defer init of angular app using manual initialization, instead of auto init with ng-app attribute.

// define some service that has `$window` injected and read your data from it
angular.service('myService', ['$window', ($window) =>({   
      getData() {
          return $window.myData;
      }
}))    

const callService = (cb) => {
   $.ajax(...).success((data)=>{
         window.myData = data;
         cb(data)
   })
}

// init angular app 
angular.element(document).ready(function() {
       callService(function (data) {
          doSomething(data);
          angular.bootstrap(document);
       });
});

where callService is your function performing AJAX call and accepting success callback, which will init angular app.

Also check ngCloak directive, since it maybe everything you need.

Alternatively, when using ngRoute you can use resolve property, for that you can see @honkskillet answer

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

7 Comments

No, run isn't the answer, as it's synchronous. Manual initialization is the only answer I've yet found that will resolve an async function whose output is needed app-wide. The question is: how do you address the properties of your services from the initialization function, so that you can store the output of the async call in something other than a global variable?
@XMLilley actually no-how. there is ng-init but value from it is not accessible in .config so only global variables.
I'm a little confused about where to call this. Is this called within the ng-init where the ng-app directive is added? Or, is this a top-level angular call (like where one would call angular.module...)?
Ignore that, needed to do more research. For those who need more help, this documentation is a good jump start (but like all angular documentation...it leaves one wanting more) docs.angularjs.org/guide/bootstrap
@jbenowitz that is how you actually bootstrap your angular app. angular.element.ready runs passed function on DOMReady and function suppose to call your code which will do some async stuff and call angular.bootstrap() in the end
|
9

even better than manually bootstrapping (which is not always a bad idea either).

angular.module('myApp', ['app.services'])
   .run(function(myservice) {
      //stuff here.
   });

3 Comments

Is it possible to delay the app until a promise returned by run is being resolved or rejected?
This won't work with any asynchronous call. If a user hits F5 somewhere within the app, the service will most likely beat the app.run in its attempt to get to the data, causing errors. AngularJS desperately needs a universal resolve, a resolve that prevents any controller from executing until its promise returns. That would be perfect for absolutely necessary, app-wide data.
Interesting point Tim. I wonder if inlining data in the host page would solve that.
4

As I said in the comments, it would be a lot easier to handle an unloaded state in your controller, you can benefit from $q to make this very straightforward: http://jsfiddle.net/g/g75XQ/4/

if you want to make something in the controller when user is loaded: http://jsfiddle.net/g/g75XQ/6/

EDIT: To delay the route change until some data is loaded, look at this answer: Delaying AngularJS route change until model loaded to prevent flicker

3 Comments

Ok I guess I will do something like that, but I hoped there was a way like youtube.com/watch?v=P6KITGRQujQ&feature=player_embedded but without the routes.
ok actually you want to delay the location (route) change until some data is loaded, I don't know how to do this, you'll perhaps have more responses in the AngularJS google groups
this question looks like what're looking for: stackoverflow.com/questions/11972026/… , you can define wich promises it should wait before loading the route
4

The correct way to achieve that is using resolve property on routes definition: see http://docs.angularjs.org/api/ngRoute.$routeProvider

then create and return a promise using the $q service; also use $http to make the request and on response, resolve the promise.

That way, when route is resolved and controller is loaded, the result of the promise will be already available and not flickering will happen.

1 Comment

Its very unfortunate that this is not the accepted answer, as it is the simplest approach without needing to manually boostrap on load. $q and promises are the way to go, whether you choose to use ngRoute, ui-router or even a controller on its own(say with ng-if based on a condition that is set upon promise resolution)
1

You can use resolve in the .config $routeProvider. If a promise is returned (as it is here), the route won't load until it is resolved or rejected. Also, the return value will be available to injected into the controller (in this case Somert).

angular.module('somertApp')
  .config(function($routeProvider) {
    $routeProvider
      .when('/home/:userName', {
        /**/
        resolve: {
          Somert: function($q, $location, Somert) {
            var deferred = $q.defer();
            Somert.get(function(somertVal) {
              if (somertVal) {
                deferred.resolve(somertVal);
              } else {
                deferred.resolve();
                $location.path('/error/'); //or somehow handle not getting
              }
            });
            return deferred.promise;
          },
        },
      });
  });

Comments

0

There are a few ways, some more advanced than others, but in your case ng-hide does the trick. See http://jsfiddle.net/roytruelove/g75XQ/3/

2 Comments

Im interested in a way that controller is also delayed until user has loaded.
No not sure this is possible (or advisable?), but you can something close if you hold off on initing the whole app as vittore mentioned below.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.