I\'m trying to DRY in $stateProvider and prevent adding the same auth function in each resolve. I\'ve created decorator that in each state change w
If I understand your requirement correctly, we can use native UI-Router built-in decorator:
Allows you to extend (carefully) or override (at your own peril) the stateBuilder object used internally by $stateProvider. This can be used to add custom functionality to ui-router, for example inferring templateUrl based on the state name... (read more in the doc)
There is a working plunker
So, we can have this var auth
var auth = ['AuthService', '$stateParams',
function(AuthService, $stateParams) {
//how to invoke this function on needed states?
if (AuthService.isAuthenticated()) {
return AuthService.me();
} else {
return false;
}
}
];
And here we just use decorator with some "IF" logic
.config(['$stateProvider',
function($stateProvider) {
$stateProvider.decorator('views', function(state, parent) {
var result = {},
views = parent(state);
// some naive example when to not inject resolve
if (state.name === "home") {
return views;
}
// child already has that in parent
if (state.name.indexOf(".") > 0) {
return views;
}
angular.forEach(views, function(config, name) {
// here inject the resolve (if not existing)
config.resolve = config.resolve || {};
// and extend it with the auth stuff above
config.resolve.auth = auth;
result[name] = config;
});
return result;
});
}
])
And later few our states, which will be extended by the above stuff
$stateProvider
.state('home', {
url: "/home",
templateUrl: 'tpl.html',
})
.state('parent', {
url: "/parent",
templateUrl: 'tpl.html',
controller: 'SharedCtrl',
})
.state('parent.child', {
url: "/child",
templateUrl: 'tpl.html',
controller: 'SharedCtrl',
});
Check it in action here
I realized that $delegate.current object contains only raw stateProvider config data. To wrap resolve function I add my function to $delegate.$current on each state change.
$provide.decorator('$state', function($delegate, $rootScope) {
$rootScope.$on('$stateChangeStart', function(event, state, params) {
if ($delegate.current === "err404" || $delegate.current === "login" || $delegate.current === "register") {
return;
}
console.log("decorator", $delegate);
$delegate.$current.resolve["auth"] = ['AuthService', '$stateParams', function(AuthService, $stateParams) {
if (AuthService.isAuthenticated()) {
console.log('AuthService.me()');
return AuthService.me();
} else {
return false;
}
}]
});
return $delegate;
});
Update
I found related discussion on github, you can add universal resolve function into toState param:
app.run(['$rootScope', '$state',
function($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(event, toState) {
if (toState.name === "login" || toState.name === "register") {
return;
}
toState["resolve"]["auth"] = ['AuthService', '$stateParams', function(AuthService, $stateParams) {
if (AuthService.isAuthenticated()) {
return AuthService.me();
} else {
return false;
}
}];
});
}
]);