Dependency Injection for $routeProvider resolve functions.

3,904 views
Skip to first unread message

Stephen Pitchford

unread,
Aug 27, 2012, 12:55:52 AM8/27/12
to ang...@googlegroups.com
Hi there,

I am working with some data that needs to be resolved before I load a partial. I came across this post on stackoverflow: 


Thich shows you can create a function that will resolve before a route is loaded. However I am wanting to know, how  would you inject dependencies into the resolve functions attached to controllers. Such as Phone, $q, and $defer in this example. 

I know with injecting dependencies into a controller you can use:

   myController.$inject= [];

How would you do the same but for a resolve function that is attached to a controller.

Thanks,
Stephen
Message has been deleted

James Andersen

unread,
Sep 7, 2012, 1:40:01 PM9/7/12
to ang...@googlegroups.com
I believe you'd do it with the $inject array that you noted:

So this (from the linked SO sample):
PhoneListCtrl.resolve = {
  phones: function(Phone) {
    return Phone.query();
  },
  delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }
}
Would become this:
var delayFunc = function($q, $defer) {
   // function body here
};
delayFunc.$inject = ['$q', '$defer'];
PhoneListCtrl.resolve = {
  phones: function(Phone) {
    return Phone.query();
  },
  delay: delayFunc
}

James Andersen

unread,
Sep 7, 2012, 1:43:02 PM9/7/12
to ang...@googlegroups.com
Sorry for the strange formatting...   Also wanted to note that you'd only need to add the $inject array to ensure DI still works with a minified script.   The SO sample would work as is if not minified.   

Stephen Pitchford

unread,
Sep 7, 2012, 2:05:44 PM9/7/12
to ang...@googlegroups.com
Thank you.. I understand the approach now. However, as a note, the function delayFunc cannot be declared in that notation. Declaring the function as a variable results in angular throwing an error saying a Service is expected.

If you declare delayFunc as

function delayFunc() {

}

it seems to be happy with it and throws no errors.

Thanks for the help.
Steve

Alec Sorensen

unread,
Feb 18, 2013, 3:24:53 PM2/18/13
to ang...@googlegroups.com
In this example, where is the actual data that will populate the phones coming from?  Is there a service called 'Phone' with that returns a function query()?

Alec Sorensen

unread,
Feb 18, 2013, 3:31:42 PM2/18/13
to ang...@googlegroups.com
Is it best practice to base the resolve off of the controllers (ctrl.resolve)?  Or, if you have several controllers checking for data from a service, would be appropriate to have a service.resolve?  That's what I was picking up from the $routeProvider docs, but I'm super new.

I realize it may not matter for a functioning program, but I'm trying to figure out best practices for organizing my angular code.


On Friday, September 7, 2012 11:40:01 AM UTC-6, James Andersen wrote:

Alec Sorensen

unread,
Feb 18, 2013, 3:42:18 PM2/18/13
to ang...@googlegroups.com
Sorry, last question (maybe): if you had more than one controller that required a resolve, would you have to define the delay function for each of them?  Or could subsequent resolve objects contain only:

delay: delayFunc($q, $defer)

rather than:

delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }


On Sunday, August 26, 2012 10:55:52 PM UTC-6, Stephen Pitchford wrote:

Saverio Trioni

unread,
Feb 22, 2013, 6:50:38 AM2/22/13
to ang...@googlegroups.com
Hi Alex. I have more or less the same issue, to delay a route change based off a service having some data loaded. Currently my solution is the following:

- create a constructor method on the view controller (not the service) (call it loadedPromise) that
  - creates a defer attribute on the same controller ($_loadedDefer)
  - returns its promise
- in the controller instance, watch the actual services for the data being loaded. When the all the needed data is loaded, resolve the $_loadedDefer with true

Of course this has some drawbacks, and I still haven't tried for corner cases, but I'd like to have your take on this approach.

Bye and thanks

Saverio

Stephen Pitchford

unread,
Feb 22, 2013, 2:57:51 PM2/22/13
to ang...@googlegroups.com
If you are wanting to have a more globally accessible resolve function, you perhaps do not need to attach it to a controller at all. Otherwise you could attach it to one of the global wrapper controllers that you would normally have such as the "main" controller which is used to capture global site events and bind things to $rootScope.

You could just create a single resolve function that does some common task and use it as you said with

delay: delayFunc($q, $defer)
If you are wanting to get a basci idea of how to use resolve functions. I would suggest taking a look at http://egghead.io/ which is a series of videos made by John Lindquist. I've found that he explains the concepts well so take a look to see how he does it.

Marco Rinck

unread,
Feb 22, 2013, 3:30:28 PM2/22/13
to ang...@googlegroups.com
I'm not really sure where the current discussion is at, but to answer the original question how to inject some service in a resolve function, I'm doing it like this: 

$routeProvider.when('/', {
templateUrl:'views/main.html',
controller:'MainController',
resolve: {
recentPosts: ['$q', 'backendService', function($q, backendService){
var deferred = $q.defer();
backendService.getRecentPosts().then(
function(data) {
var result = data.result;
deferred.resolve(result);
},
function(error) {
deferred.reject(error);
}
);
return deferred.promise;
}]
}
})

You can put the resolve function as a last element into an array, where every element before that, is a name for DI. That way, it can also be used with minification. 

The result which is used to resolve the promise, can be used then in the depency injection for the controller of the route (in my case recentPosts).

Marco 

Saverio Trioni

unread,
Feb 22, 2013, 5:30:08 PM2/22/13
to ang...@googlegroups.com
This is in fact the only way I managed it to work. Resolving a promise inside the controller is impossible, given that the controller is not instanciated until the promise is resolved......

So the main point is that you can use any injected function as the value of a resolve, using your services too.

This is the way I have done it:

$routeProvider.when "some/route"
  templateUrl: "some/url.html"
  controller: "SomeController"
  resolve:
    myServiceLoaded: ["myService", (svc) -> svc.lockDefer.promise]

and in the service

module.factory "myService", ["$q", (q) ->
  @lockDefer = q.defer()
  # now load something...
  $resource('/user/:userId').query ->
    @lockDefer.resolve true

So the lockDefer deferred is unlocked in the service as soon as the data is ready. Using the composability of promises, you can resolve the lock for that services after any set of other promises have returned, allowing to load a bunch of data before your app is ready.
Message has been deleted

Josh Iverson

unread,
Feb 23, 2013, 1:49:38 AM2/23/13
to ang...@googlegroups.com
I would also take a look here http://www.youtube.com/watch?v=P6KITGRQujQ John has a more detailed example which I don't think he has on egghead.io. Ideally you should decouple your resolve function into a service or factory separate from the controller therefore you can inject any resources you are using into the service. 

Reply all
Reply to author
Forward
0 new messages