Update 12/10/13: Hey, this article was picked up by Angular News. Thanks, Brian!
Both AngularUI Router and Angular's own ngRoute support the concept of a resolve, an optional map of dependencies which should be injected into a controller associated with a particular route or state. If any of these dependencies are promises, the router will wait for them all to be resolved (or one to be rejected) before the controller is instantiated. This can be a very useful feature when you want to get some data and be sure it's available to a controller before you do any processing.
Resolves can be services, or functions that return a promise, an object, or a primitive. We could return a promise by using Angular's $q service like this:
demo: Example 1
Resolve a resource
To help you interact with RESTful server-side data sources, Angular provides the $resource service. Your resources can be injected into a resolve and you can return a resource invocation like this:
Assuming a resource like this exists:
demo: Example 2
When using a resource in your resolve instead of a simple promise, you may be surprised that the resource resolves instantly to an empty object. This is actually the intended behavior, to facilitate data binding. From the docs:
invoking a resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data...
The idea here is that you can bind a $scope model directly to the returned resource data and let Angular handle updating the view when the data is available. Done!
But, what if you need to do some processing on this data in the controller before its exposed to $scope? Or what if some of your controller logic depends on this data? Well, since angular resources are services, you could forgo the resolve and inject them into the controller directly. In the controller, you can invoke the resource as needed and wrap any data-dependent logic in the .then() method of the returned $promise. However, if your state or route change logic depends on this data, there is another option.
Resolve a resource's promise
You can make sure a resource is fully resolved before your route/state change by returning the original $promise contained in the object returned by the resource invocation instead of the object itself.
Assuming a resource like this exists:
demo: Example 3
Resolve multiple resource promises
And what if you want to fully resolve multiple resource promises before your route/state change? No problem, just use $q.all to wait for all resource promises to resolve.
Assuming these resources exist:
demo: Example 4
Notes
Note that although we've used AngularUI Router to illustrate resolves, you can use ngRoute the same way. Also, remember that a promise may be rejected as well as resolved. Make sure to handle rejections (and progress, if desired) as necessary.
Comments