I have some routes defined like this :
$stateProvider
.state('app', {
url: '/',
abstract: true,
views: {
'menuContent': {
templateUrl: 'templates/home.html'
}
}
})
.state('app.restricted', {
url: '/restricted',
views: {
'content': {
templateUrl: 'templates/restricted/restricted-dashboard.html',
controller: 'RestrictedController as vmRestricted'
}
},
resolve: {
isGranted: 'isGranted'
}
})
.state('app.restricted.pending', {
url: '/pending',
views: {
'tabsView': {
templateUrl: 'templates/restricted/restricted-manage-pending.html',
controller: 'RestrictedPendingController as vm'
}
},
resolve: {
isGranted: 'isGranted'
}
})
.state('app.restricted.devices', {
url: '/devices',
views: {
'tabsView': {
templateUrl: 'templates/trusted/restricted-manage-devices.html',
controller: 'RestrictedDevicesController as vm'
}
},
resolve: {
isGranted: 'isGranted'
}
})
.state('app.grant', {
url: '/grant-access',
views: {
'content': {
templateUrl: 'templates/grant-access.html',
controller: 'GrantAccessController as vm'
}
}
})
;
In these routes I have a restricted area and a grant access page to grant access to the restricted area.
When the isGranted
resolve provider is rejected I redirect to the app.grant
route.
This is the code doing this :
$rootScope.$on(AngularEvents.STATE_CHANGE_ERROR, _onStateChangeError);
function _onStateChangeError(event, toState, toParams, fromState, fromParams, error){
switch (error) {
case 'accessRejected':
$state.go('app.grant');
break;
}
}
Here is the code of my isGranted
provider :
(function() {
'use strict';
angular.module('app')
.provider('isGranted', isGrantedProvider);
isGrantedProvider.$inject = [];
function isGrantedProvider() {
this.$get = isGranted;
isGranted.$inject = ['$q', '$log', 'grantService'];
function isGranted($q, $log, grantService){
$log.log('isGrantedProvider');
if (grantService.isGranted()) {
return $q.when(true);
} else {
return $q.reject('accessRejected');
}
}
}
})();
(grantService.isGranted()
just returns a boolean value)
The first time I go to the app.restricted
route with $state.go('app.restricted')
the provider is executed.
The route is rejected because the access is not granted and we are redirected to the app.grant
route.
In this page, the user can log in and have access to the restricted area. Once the user is logged in we redirect him to the app.restricted.pending
route but the resolve is not called and the route is rejected and we are redirected to the app.grant
route again, whereas the access was granted.
Why is the resolve not called? Is there a way to force it?
EDIT
I have new information after some testing.
I saw that the resolve is not called the second time only when it is a service:
This resolve is always executed when we enter the state:
state('app.restricted', {
url: '/restricted',
views: {
'content': {
templateUrl: 'templates/restricted/restricted-dashboard.html',
controller: 'RestrictedController as vmRestricted'
}
},
resolve: {
isGranted: ['$log', function($log) {
$log.log('RESOLVE');
}]
}
})
But this resolve is only executed once even when I enter again to the state:
state('app.restricted', {
url: '/restricted',
views: {
'content': {
templateUrl: 'templates/restricted/restricted-dashboard.html',
controller: 'RestrictedController as vmRestricted'
}
},
resolve: {
isGranted: 'isGranted'
}
})
angular.module('app')
.provider('isGranted', isGrantedP);
isGrantedP.$inject = [];
function isGrantedP() {
this.$get = isGranted;
isGranted.$inject = ['$q', '$log'];
function isGranted($q, $log){
$log.log('RESOLVE');
}
}
Why isn't this service called each time? Is it because a service is a singleton? How should I proceed?
After a lot of investigations and testing I found the solution!
First, let's see why it is not working
As mentioned in the docs (http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$stateProvider), if the resolve is a string, then it corresponds to a service
And as mentioned in the angularjs docs (https://docs.angularjs.org/guide/providers), all services are singletons, meaning that it will be instantiated only once
Why is it important?
Because resolves do not call a function inside our service. They just use the return value of the instantiated service. BUT because our service will be instantiated only once, the return value will always be the same! (because our service initialization is only called once)
What can we do?
From my tests I could see that a resolve defined like this:
is always executed, so we can write them this way to make it work correctly.
But how can I do if I want to use my service?
The best working solution I found to be able to use my service and have a syntax that looks similar to this one:
myResolve: 'myResolveService'
is to declare my resolve like this:And my service like this:
This code can also be adapted for resolves that return a promise:
Resolve:
Service: