Reputation: 1300
In my parent state I have a resolve
. Currently, I do not inject the resolve key in any child state.
I assumed that my child state would not wait for this resolve/promise to be resolved. But when I set a timeout, I can see my child state does wait.
Is this correct behaviour?
.config(function ($stateProvider, $urlRouterProvider, STATES) {
$stateProvider
.state(STATES.ROOT, {
abstract: true,
template:'<div ui-view=""></div>',
resolve: {
UserService: 'UserService',
userDetails: function(UserService){
return UserService.getUserProfile();
}
}
})
.state(STATES.BILLING, {
url: '/bill/checkClientContext.html',
templateUrl: 'bill/checkClientContext.html',
controller: 'BillingController'
})
UserService.js
'use strict';
angular.module('nabc.data')
.service('UserService', ['AjaxService', '$timeout','$q', function(AjaxService, $timeout, $q) {
var getUserProfile = function() {
var promise = AjaxService.get('userProfile');
var deferred = $q.defer();
$timeout(function(response){
deferred.resolve(response);
}, 5000);
return deferred.promise;
};
return {
getUserProfile: getUserProfile
};
}]);
As you can see above the BillingController
does not inject in userDetails
. But when i set a timeout in the userService, i see that my billing state does wait.
Upvotes: 2
Views: 6197
Reputation: 7998
Here is a short example, let's say we have parent state P with resolve section and two child states C1 and C2.
First case - you navigate to P.C1 state from separate state or just hit the corresponding URL in the browser. The order will be following:
Second case - you navigate from P.C1 to P.C2 in this case P don't need to be loaded, so only C2 controller is loaded.
Upvotes: 1
Reputation: 123901
The answer could be found here (let me cite few lines and snippet):
New in version 0.2.0
Child states will inherit resolved dependencies from parent state(s), which they can overwrite. You can then inject resolved dependencies into the controllers and resolve functions of child states.
$stateProvider.state('parent', {
resolve:{
resA: function(){
return {'value': 'A'};
}
},
controller: function($scope, resA){
$scope.resA = resA.value;
}
})
.state('parent.child', {
resolve:{
resB: function(resA){
return {'value': resA.value + 'B'};
}
},
controller: function($scope, resA, resB){
$scope.resA2 = resA.value;
$scope.resB = resB.value;
}
So, as we can see in the doc, resolve on parent could be used on a child. And that is in fact reason, while we have to wait for this to be resolved... before we can continue.
But I would say, that this is expected. The more fancy feature would be:
if the child state has defined
resolve
- and parent does not - it would be great to see parent views rendered, and let only child's views to wait.
This is (as far as I know) planned in the near future as a feature...
To get the answer:
Must all the resolves - declared in current state and all its parent states - be awaited to continue with current? Even if current state controllers do not require any of these values?
Do observe the code: state.js
function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
// Make a restricted $stateParams with only the parameters that apply to this state if
// necessary. In addition to being available to the controller and onEnter/onExit callbacks,
// we also need $stateParams to be available for any $injector calls we make during the
// dependency resolution process.
var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
var locals = { $stateParams: $stateParams };
// Resolve 'global' dependencies for the state, i.e. those not specific to a view.
// We're also including $stateParams in this; that way the parameters are restricted
// to the set that should be visible to the state, and are independent of when we update
// the global $state and $stateParams values.
dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
var promises = [dst.resolve.then(function (globals) {
dst.globals = globals;
})];
if (inherited) promises.push(inherited);
// Resolve template and dependencies for all views.
forEach(state.views, function (view, name) {
var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
injectables.$template = [ function () {
return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: options.notify }) || '';
}];
promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) {
// References to the controller (only instantiated at link time)
if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
var injectLocals = angular.extend({}, injectables, locals);
result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
} else {
result.$$controller = view.controller;
}
// Provide access to the state itself for internal use
result.$$state = state;
result.$$controllerAs = view.controllerAs;
dst[name] = result;
}));
});
// Wait for all the promises and then return the activation object
return $q.all(promises).then(function (values) {
return dst;
});
}
I decided to cite the complete method here, but the most important parts are: declaration of the var promises = [];
. This array is later extended with all resolves required to continue promises.push($resolve.resolve(...
and finally, untill all the stuff is not done, no result - return $q.all(promises)
Upvotes: 4