I'm trying to implement a user auth check and access check system in my app, however I keep hitting roadblocks. I think I have it the correct way this time but I have one last hurdle.
A little background: I tried putting all of the code into the $rootScope.on($startChangeStart) and it worked, horribly... but it worked. The route was always redirected but due to the auth check on the backend it displayed the first request page for 1/2 a second and then the redirect page every time. Thus I tried 'pausing' page load by calling evt.preventDefault() right at the start of the $startChangeStart function, which worked, but trying to put the user back to the original route afterwards caused an infinite loop in the router.
So after more research and reading a lot of stack posts I'm certain that 'resolve:' is the proper place to put the auth check to ensure the page is not loading while it occurs, and then redirect the user if needed from the $startChangeStart. ($state and event are always undefined in my attempts to inject them into a resolve function) It seems like the winning combination.
My problem: I have the resolve on the root state in my app: 'main'
This was to avoid code redundancy, however I cannot determine how to access the root state's properties, and therefore the resolve result, from the $stateChangeStart function. The toState is the child state, while the fromState is either the previous state or an abstract state with the '^' route...
Do I have to put the resolve on every child state for this to work, or is there a way to access the root state from this point?
Basic app setup:
angular.module('App', ['ui.router', 'ui.bootstrap', 'ui.event', 'AngularGM', 'ngResource'])
.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider){
$urlRouterProvider
.when('/home', '/')
.when('', '/')
.when('/sign-up/joe', '/sign-up')
.otherwise('/');
$stateProvider
.state('main', {
url: '',
abstract: true,
templateUrl: 'views/main.html',
controller: 'MainCtrl',
resolve: {
checkAccess: ['accountService', function(accountService) {
accountService.checkAuth(function(){
accountService.checkAccess(function (access){
return access;
});
});
}]
}
})
.state('main.home', {
url: '',
abstract: true,
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
})
.state('main.home.index', {
url: '/',
templateUrl: 'views/home/index.html'
});
.run(['$rootScope', '$state', '$stateParams', 'accountService', function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
console.dir(toState);
console.dir(toParams);
console.dir(fromState);
console.dir(fromParams);
if (!toState.checkAccess.allowed) {
event.preventDefault();
$state.transitionTo(toState.checkAccess.newState);
}
});
}]);
This is the output from the console.dir() calls on the two state objects:
Object
name: "main.home.index"
templateUrl: "views/home/index.html"
url: "/"
__proto__: Object
Object
controller: "PlacesCtrl"
name: "main.places.search"
templateUrl: "views/places.html"
url: "/places"
__proto__: Object
Update
Oops, forgot to mention AngularJS version is v1.2.0-rc.2
$state.current console.dir()
Object
abstract: true
name: ""
url: "^"
views: null
__proto__: Object
Yes, I believe you can access root state from the $stateChangeStart
function.
When using pure AngularJS I normally use current.$$route
For example, using the following route
.when('/home', {
title:'Home',
bodyClass: 'meetings',
controler: 'HomeCtrl'
})
I can access the root state like so
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
if (current.$$route) {
$rootScope.title = current.$$route.title;
$rootScope.bodyClass = current.$$route.bodyClass;
}
});
Using ui-router
it's just a bit different as it's called $state.current
. And you can access all the properties associated to whatever route you hit (e.g: $state.current.url)
So on your code you could have something like this
.run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
console.log($state.current.url);
});
}]);
You need not to use resolve
. Take a look at my solution:
app.run ($rootScope, $state, Auth, ngDialog) ->
$rootScope.$on '$stateChangeStart', (e, to) ->
if to.authRequired and not Auth.isAuthenticated()
e.preventDefault()
Auth.currentUser().then(
(user) ->
$state.go(to)
(failure) ->
$rootScope.storedState = to
$state.go('main')
ngDialog.closeAll()
ngDialog.open
template: 'modals/login.html'
controller: 'loginCtrl'
className: 'ngdialog-theme-default'
)
I use angular_devise and ngDialog but they are optional and you can implement it with your own user's service.
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