Ok, so I have a state for the url "/securepage" which I need to check whenever a user tries to access it. So I read there is an onEnter function I can use. But I cannot seem to get hold on the scope nor a service from there. What am I doing wrong?
.state('securepage', {
url: "/securepage",
template: securepage.html,
onEnter: function(){
// if (!$scope.main.isAuthenticated) $state.go("/login");
// if (!myLoginService.currentUser()) $state.go("/login");
Current options as I see it are to use resolve and/or check the authentication in the controller. But wouldnt an auth check be better placed in onEnter?
I ran into a similar problem today. Spent a whole day and finally came up with a workable solution other than the ones already suggested here.
My main goal is to find easy and effective way to selectively secure certain specific webpages. The security check needs to be performed before the HTML or any of the relevant controllers are loaded or called. If the check fails, the page may be forwarded to elsewhere without any side effects from the other controllers.
I tried the other suggested approaches. Each one has their own set of problems:
Using OnEnter:
Using $rootScope.$on('$stateChangeStart'):
My solution is to use a resolve function to define a promise that will cause the view controllers to wait for the deferred to complete before they are called. This work perfectly for blocking the controller from starting in an asynchronous fashion.
Here's a rough outline of the code I used:
.config(['$stateProvider', function ($stateProvider) {
// Handler for Restricting Access to a page using the state.resolve call
var accessRestrictionHandler = function($q, $rootScope, $state) {
var deferred = $q.defer();
// make sure user is logged in
asyncCheckForLogin(function(status) {
if (status != "Logged In") {
// You may save target page URL in cookie for use after login successful later
// To get the relative target URL, it is equal to ("#" + this.url).
// The "this" here is the current scope for the parent state structure of the resolve call.
$state.go("loginPage");
}
else // if logged in, continue to load the controllers. Controllers should not start till resolve() is called.
deferred.resolve();
}.bind(this));
return deferred.promise;
};
$stateProvider
.state('userProfile', {
url: '/userProfile',
views: {
'main': {
templateUrl: 'userProfile.html',
controller: 'userProfileCtrl'
}
},
// SIMPLY add the line below to all states that you want to secure
resolve: { loginRequired : accessRestrictionHandler }
})
.state(.... some other state)
.state(.... some other state);
}]);
I hope this will help some of you out there.
Another approach would be to have a service/controller listen to the "$stateChangeStart" event. There, you could check if the called state needs authentication and reroute the request. Here's a snippet:
$rootScope.$on('$stateChangeStart', function (event, nextState, currentState) {
if (!isAuthenticated(nextState)) {
console.debug('Could not change route! Not authenticated!');
$rootScope.$broadcast('$stateChangeError');
event.preventDefault();
$state.go('login');
}
});
isAuthenticated
could hold the call to your services, check nextState.data
for authentication-related properties, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With