How to resolve data before application start?

17,947 views
Skip to first unread message

Sergey Chico

unread,
Dec 24, 2012, 6:46:54 AM12/24/12
to ang...@googlegroups.com
I need to load some data from server before all controllers init. Now I use the resolve of $routeProvider. But this way I need to include my resolve function returning promise into every resolve for every route. This looks not DRY-way. Any thoughts?

Pawel Kozlowski

unread,
Dec 24, 2012, 6:57:30 AM12/24/12
to ang...@googlegroups.com
Use the .run() block of a module.
Send a jsfiddle or plunk if you need help wit the code.

Cheers,
Pawel


On Monday, December 24, 2012, Sergey Chico wrote:
I need to load some data from server before all controllers init. Now I use the resolve of $routeProvider. But this way I need to include my resolve function returning promise into every resolve for every route. This looks not DRY-way. Any thoughts?

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en-US.
 
 


--
Question? Send a fiddle (http://jsfiddle.net/pkozlowski_opensource/Q2NpJ/) or a plunk (http://plnkr.co/)
Need help with jsFiddle? Check this: http://pkozlowskios.wordpress.com/2012/08/12/using-jsfiddle-with-angularjs/

Looking for UI widget library for AngularJS? Here you go: http://angular-ui.github.com/

Sergey Chico

unread,
Dec 24, 2012, 7:37:09 AM12/24/12
to ang...@googlegroups.com
I don't think jsfiddle or plunkr will be useful. I need something like 
    app.run(['$rootScope', '$http', function($rootScope, $http) {
        $http.get('some/url').success(function (data) {
            // make the rest of application init (e.g. controllers can use the success function data)
            // for example
            $rootScope.veryImportantDataApplicationNeedsOrElseItMustNotInit = data;
            // then make controllers init so they can use $rootScope.veryImportantDataApplicationNeedsOrElseItMustNotInit.someProperty
        });
    }]);

Now I write code this way:
        routeProvider.when('/path1, {
            templateUrl: '/partials/path1.html',
            controller: 'path1Controller',
            resolve: // includes function returning $http promise with veryImportantDataApplicationNeedsOrElseItMustNotInit
        });
        routeProvider.when('/path2, {
            templateUrl: '/partials/path2.html',
            controller: 'path2Controller',
            resolve: // includes function returning $http promise with veryImportantDataApplicationNeedsOrElseItMustNotInit
        });
.............................
.............................
        routeProvider.when('/path1000000000000, {
            templateUrl: '/partials/path1000000000000.html',
            controller: 'path1000000000000Controller',
            resolve: // includes function returning $http promise with veryImportantDataApplicationNeedsOrElseItMustNotInit
        });
...and there are 2 problems:
1. I repeat myself every of 1000000000000 paths for routeProvider
2. Controllers which are directly in index.html (using ng-controller) still can not use someProperty of veryImportantDataApplicationNeedsOrElseItMustNotInit or I need to place promise.then(...) into them
So that looks VERY ugly and I just want to know is there any other ways to preload the data for my application.


понедельник, 24 декабря 2012 г., 15:57:30 UTC+4 пользователь Pawel Kozlowski написал:
Use the .run() block of a module.
Send a jsfiddle or plunk if you need help wit the code.

Cheers,
Pawel

On Monday, December 24, 2012, Sergey Chico wrote:
I need to load some data from server before all controllers init. Now I use the resolve of $routeProvider. But this way I need to include my resolve function returning promise into every resolve for every route. This looks not DRY-way. Any thoughts?

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+unsubscribe@googlegroups.com.

Peter Bacon Darwin

unread,
Dec 24, 2012, 9:18:34 AM12/24/12
to ang...@googlegroups.com
In our demo app we tried a mechanism of wrapping the $routeProvider so that when we called when() on "our" $routeProvider, it added other stuff onto the route for us automatically:

(I see this as a common pattern moving forward as there are a number of scenarios where this saves typing and make things more DRY.  Unfortunately, the current config/provider declaration mechanism does not make this a very readable piece of code!  I have suggested to Igor et al that we could have a providerHelper concept that can be injected into providers and config blocks but doesn't actually create a service itself).

Another option could be to hook in to a $http responseInterceptor and ensure that your data is there before allowing the current response to return?

Pete


To unsubscribe from this group, send email to angular+u...@googlegroups.com.

Jeff Nadler

unread,
May 22, 2013, 12:52:17 PM5/22/13
to ang...@googlegroups.com
I know that this is an old thread but I just wanted to chime in - we're facing this exact same problem and it's an area where Angular has a notable gap.   

I'm using resolve *on every route* to solve this problem just like Sergey, which means I have to repeat myself in every route+controller.   It stands out like a sore thumb in our otherwise elegant code.

I think there's a meaningful opportunity for improvement here.

Dennis Sänger

unread,
Jun 24, 2013, 5:41:48 AM6/24/13
to ang...@googlegroups.com
+1

Same problem here, I'am handling it like this:

I'am currently loading my "important data" in the app controller ( = the root controller ) and assign the promise to a scope variable. Since every other controller is a child controller of my app controller, I can access my promise and through it my data in every controller and in their templates. Since angular event handles the promise api in templates for me, I can use the variable assigned in my AppController scope directly in the templates of my child controllers.

If I have some spare time, I will write a short howto on this matter.

Tim Hardy

unread,
Sep 25, 2013, 11:19:58 PM9/25/13
to ang...@googlegroups.com
I just got hit in the face with this problem on my current project.  We have to load up FeatureToggles to turn features on and off throughout the app.  This needs to happen before just about anything else.  I don't want to use manual bootstrap because that would mean not being able to use the nicely angular services that we've written to get this data (services with dependencies, etc).

I'd love to see a sample of your controller that does this.  No howto necessary, just a jsfiddle or plunker or some sample code somewhere.

I'm having to go down the path that involves a resolve in every route - not pretty.

Witold Szczerba

unread,
Sep 26, 2013, 5:04:48 PM9/26/13
to ang...@googlegroups.com

What is your use case for this?
I had similar problem, I mean, back then I though I had, with my authorization system.
I had to fetch data from server to be able to figure out what is the user allowed to see, like what options in menu and what actions were possible to they.

The solution was to back everything with promises. Promises are very nice, they let you do things at once and there is no need to wait for a service to return data before the consumers of the service run.

So, the service triggers the http request and immediately return promise. If your controllers need the response, they ask the promise to resolve with callback. There is no need for the controllers to actually be postponed. They will initialize, use the promise returned by the service and once the data are available - they continue.
If the data is available before controller was initialized, the promise will trigger callback immediately. If not, the callbacks will wait. It worked for me.

Regards,
Witold Szczerba
---
Sent from my mobile phone.

To unsubscribe from this group, send email to angular+u...@googlegroups.com.

Tim Hardy

unread,
Sep 26, 2013, 11:00:34 PM9/26/13
to ang...@googlegroups.com
FeatureToggles.  Is a feature turned on or off?  If it's off, then nothing related to it should be shown - buttons, tabs, grids, anything.  Directives are an elegant way to hangle this, just place a data-feature="SomeFancyFeature" on related elements.

Callbacks don't work well with directives.  Directives don't wait.  They bind, and if the data they need isn't available when they bind, then you have to make a choice.  Do you show the element and hide it later?  Do you hide it and show it later?  It's so much easier to simply accept that in scenarios, there really is data that is so important that some things need to wait for it.  It's simply not worth it to let the controller and view partially "do" anything.

Luke Kende

unread,
Sep 27, 2013, 1:52:53 AM9/27/13
to ang...@googlegroups.com
Similar to Dennis's method, I am using the app.run block to initialize my data from an api service and set "ready" to true on $rootScope once all api calls have completed.  Each controller has watch on $scope.ready and fires a startController function that wraps the controller's functionality that would normally run as with route.  The partials related to the route also use ng-switch or ng-show related to a scope variable that isn't assigned until the startController fires hence I don't have to worry about directives loading prematurely.  

I don't love it, but it works well and ensures data is loaded.  Here's the idea

app.run:
----------------
$rootScope.dataReady = false;

var userSettingsLoaded = false,
   coreDataLoaded = false;

myApiService.loadUserSettings().then(function(){
  userSettingsLoaded = true;
})

myApiService.loadCoreData().then(function(){
  coreDataLoaded = true;
})

$rootScope.$watch(
  function(){ return userSettingsLoaded && coreDataLoaded },
  function(ready){ $rootScope.dataReady = ready }
)

Sample controller referenced in routeProvider for current route:
--------------------------------------------------------------------------------------
function SampleCtrl($scope, myApiService){

  $scope.$watch('dataReady',function(ready){
    if (ready){ startController() }
  })
    
  //any vars needed in controller scope here
  $scope.showTemplate = false;

  function startController(){

     $scope.data = myApiService.coreData;

     $scope.showTemplate = true;
  
    //the rest of the $scope bindings
  }
}

Sample Partial for SampleCtrl:
------------------------------------------
 <div ng-hide="showTemplate">Loading...</div>
 <div ng-show="showTemplate">
    <my-directive></my-directive>
  </div>


Not the most elegant solution, but it works.  

Sebastien Vincent

unread,
Sep 27, 2013, 1:57:47 AM9/27/13
to ang...@googlegroups.com
Maybe you can use ng-show/ng-controller combination with something like this in your sub-controller:
ready = false;
... (initialize data cleanly)
promise.then(function(){
...
ready = true;
});

You can easily encapsulate this to make your code DRY.

I don't think there is a way to resolve sub-controller via configuration. The fact that your directive doesn't need to wait for the data before binding is probably a good thing, makes it more solid.

Witold Szczerba

unread,
Sep 27, 2013, 3:03:56 AM9/27/13
to ang...@googlegroups.com

So your case is the same as my. Promises work for me very well in this scenario. Directives work very well with them as well.
Rember that you cam make entire app invisible or hidden behind some fancy "please wait" banner until every crucial data arrives (look at the API, you can resolve multiple promises in one go), so there is no need to worry how does your directives looks like when data is not yet available.

Regards,
Witold Szczerba
---
Sent from my mobile phone.

To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.

To post to this group, send email to ang...@googlegroups.com.

Cameron MacKenzie

unread,
Jan 27, 2014, 4:19:43 PM1/27/14
to ang...@googlegroups.com
Is there still no good solution for this?

It'd be really nice if all of the dependencies could be resolved in run() before the rest of the application executes. As it stands, you have to add a resolve to EVERY controller for the dependencies you need. That seems like a maintenance pain in the butt. Is there any other solution?

Witold Szczerba

unread,
Jan 27, 2014, 4:34:41 PM1/27/14
to ang...@googlegroups.com
Why would you need to add resolve to every controller? The solution
described by me works pretty well, and I have never add any resolve to
controllers...

2014-01-27 Cameron MacKenzie <cmack...@energypoints.com>:

Tewson Seeoun

unread,
Feb 14, 2014, 1:37:21 PM2/14/14
to ang...@googlegroups.com
Hi,

I'm also looking for an elegant solution for this.
In my case, I need to load some configuration variables from the backend before some services can be used.

I am aware of the resolve functionality in routeProvider.
But as a few people have said here, there must be a more elegant solution than attaching the same resolve function to every route.

Witold, an example would be as follows. Let's say in a controller there's

var someDefaultValue = service1.getDefaultValue();
service2.foo(someDefaultValue);

where the defaultValue here has to be retrieved from the backend and is used in every controller.
As far as I know, the only ways to guarantee that defaultValue is ready is either to bootstrap the app manually or pass it to the controller via routeProvider's resolve (which means it has to be done in every route).
Backing defaultValue with a promise would have worked if it were to be used in $scope.
However, defaultValue is used in another service (service2), which doesn't wait for service1 to have defaultValue initialized.

Sander Elias

unread,
Feb 15, 2014, 11:40:58 AM2/15/14
to ang...@googlegroups.com
Hi Tewson,

Have a look at this. Its still a bit rough at the edges, but it should get you started. 
It resolves a number of services, a $http and some user interaction, before firing of to any route.

Regards
Sander

Stu Salsbury

unread,
Feb 15, 2014, 11:50:30 AM2/15/14
to ang...@googlegroups.com
If you use ui-router you can create a "base" state and do all of your app initialization inc. $http stuff inbits resolve.

This has the effect of preventing all rendering while you get things done regardless of the entry point each use has into the app. Goes grat with some css/spinner-type fode that says "please wait while loading" or whatever.

Sander Elias

unread,
Feb 15, 2014, 12:24:31 PM2/15/14
to ang...@googlegroups.com
Hi Stu,

You can create a 'base-state' without any router at all. I do like ui-router, however for something like this, it is not really needed. I have just shown a way to do, that can integrate in any angular app.

Regards
Sander

tew...@gmail.com

unread,
Feb 15, 2014, 1:10:46 PM2/15/14
to ang...@googlegroups.com
Thanks, Sander. I'll try it out.

Tewson


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/F5yoJevAHNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.

To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.

Alexander Kjeldaas

unread,
Feb 15, 2014, 6:55:42 PM2/15/14
to ang...@googlegroups.com

I solve this using a double bootstrap.  I first bootstrap a "bootloader" which then bootstraps the main app.

This isolates all use of promises to the bootloader since all of these dependencies can be directly injected into the second bootloader.

The only downside to this is that angular does not support anonymous (DOM-free) bootstraps.  It would be great to be able to bootstrap without touching the DOM.

Alexander

leif hanack

unread,
Feb 18, 2014, 4:07:19 AM2/18/14
to ang...@googlegroups.com
I issued a feature request at angular: https://github.com/angular/angular.js/issues/5854

Please take a look if it matches your desire and comment accordingly.

Thanks, Leif

tew...@gmail.com

unread,
Feb 18, 2014, 5:39:51 AM2/18/14
to angular
I just had a chance to watch videos from ng-conf. It seems like a team from Google solves this problem by rendering index.html server-side.
This way they can load all the configurations they need before the AngularJS app runs.


Tewson


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/F5yoJevAHNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.

Niklas Rasmusson

unread,
Mar 11, 2014, 12:58:46 PM3/11/14
to ang...@googlegroups.com
Sander, your solution works great for delaying code from running inside controllers until you have done your initial setup. However, troubles occur when you want to put some code into route resolve object as this solution won't stop that code from running. I forked your plunkr to show you what I mean. My app relies on many route resolves so I unfortunately can't use your solution. Since many apps use route resolves to setup data for the controller I thought it'd be a good idea to share my experience, before people waste any time trying to get this solution to work with route resolves as I did.

Sander Elias

unread,
Mar 11, 2014, 3:38:01 PM3/11/14
to ang...@googlegroups.com
Hi Niklas,


Regards
Sander

Niklas Rasmusson

unread,
Mar 12, 2014, 6:07:05 AM3/12/14
to ang...@googlegroups.com
That's a nice fix. I didn't think of DI a promise like that.

gabriel troia

unread,
Oct 16, 2014, 12:49:23 PM10/16/14
to ang...@googlegroups.com
Hey guys, what do you think of this solution? http://jsfiddle.net/gabrielcatalin/cvyq0oys/

I added 2 methods: resolve() and ready() to the angular.module object, which will make this a bit cleaner. Still hacky I believe, but cleaner...

Lucian Enache

unread,
Oct 16, 2014, 3:03:35 PM10/16/14
to ang...@googlegroups.com, ang...@googlegroups.com
My two cents?

You are still thinking jquery/backbone like, why would one want to clone the whole angular object with all the implications and not use some config before run?



Sent from my iPhone
--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.

To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages