Как получить доступ к свойству "разрешение" корневого состояния UI-Router из $stateChangeStart?
Я пытаюсь внедрить проверку подлинности пользователя и систему проверки доступа в своем приложении, однако я продолжаю бить блокпосты. Я думаю, что на этот раз у меня есть правильный путь, но у меня есть последнее препятствие.
Немного фона: я попытался поместить весь код в $rootScope.on($ startChangeStart), и это сработало, ужасно... но это сработало. Маршрут всегда перенаправлялся, но из-за проверки подлинности на бэкэнд он отображал первую страницу запроса на 1/2 секунды, а затем страницу перенаправления каждый раз. Таким образом, я попытался "приостановить" загрузку страницы, вызвав evt.preventDefault() прямо в начале функции $startChangeStart, которая работала, но попытка вернуть пользователя обратно к исходному маршруту впоследствии вызвала бесконечный цикл в маршрутизаторе.
Итак, после большего количества исследований и чтения много сообщений о стеках я уверен, что "разрешить:" - это правильное место, чтобы поставить проверку подлинности, чтобы гарантировать, что страница не загружается во время ее возникновения, а затем перенаправляет пользователя, если это необходимо из $startChangeStart. ($ state и event всегда undefined в моих попытках внедрить их в функцию разрешения). Кажется, что выигрышная комбинация.
Моя проблема: у меня есть решение в корневом состоянии в моем приложении: "main"
Это было во избежание избыточности кода, однако я не могу определить, как получить доступ к свойствам состояния корня и, следовательно, результат разрешения, из функции $stateChangeStart. ToState - это дочернее состояние, а fromState - либо предыдущее состояние, либо абстрактное состояние с маршрутом "^"...
Должен ли я установить разрешение для каждого дочернего состояния для этого, или есть способ получить доступ к корневому состоянию с этой точки?
Базовая установка приложения:
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);
}
});
}]);
Это вывод из вызовов console.dir() двух объектов состояния:
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
Обновление
Ой, забыл упомянуть, что версия AngularJS - v1.2.0-rc.2
$state.current console.dir()
Object
abstract: true
name: ""
url: "^"
views: null
__proto__: Object
Ответы
Ответ 1
Да, я считаю, что вы можете получить доступ к корневому состоянию из функции $stateChangeStart
.
При использовании чистого AngularJS я обычно использую current.$$route
Например, используя следующий маршрут
.when('/home', {
title:'Home',
bodyClass: 'meetings',
controler: 'HomeCtrl'
})
Я могу получить доступ к состоянию root таким образом
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
if (current.$$route) {
$rootScope.title = current.$$route.title;
$rootScope.bodyClass = current.$$route.bodyClass;
}
});
Используя ui-router
, он немного отличается, так как он называется $state.current
. И вы можете получить доступ ко всем свойствам, связанным с любым удаленным вами маршрутом (например: $state.current.url)
Итак, на вашем коде вы могли бы что-то вроде этого
.run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
console.log($state.current.url);
});
}]);
Ответ 2
Вам не нужно использовать resolve
. Взгляните на мое решение:
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'
)
Я использую angular_devise и ngDialog но они являются необязательными, и вы можете реализовать его с помощью собственного пользовательского сервиса.
Ответ 3
Возможно ли сделать перенаправление из вашего accountService
? Если вы обнаружите, что пользователь не выполняет функции checkAuth или checkAccess, вы можете предотвратить выполнение обратного вызова и перенаправить пользователя на страницу с ошибкой (или логином).
Что-то еще, что нужно учитывать, это реализация какой-либо переменной/очереди состояний, если вы хотите перенаправить кого-то на страницу входа, чтобы обновить их авторизацию/аутентификацию, а затем вернуться в предыдущее состояние.
Ответ 4
Если вы инициализируете свое состояние стандартным пустым объектом при разрешении, вы сможете управлять им в $stateChangeStart
.
$stateProvider
.state 'home',
url: "/"
resolve: {}
...
$rootScope.$on '$stateChangeStart', (e, toState, toParams, fromState, fromParams) ->
toState.resolve.x = ->
$timeout ->
alert "done"
, 3000
См. https://github.com/angular-ui/ui-router/issues/1165
Ответ 5
Этот ответ очень поздний, но он может быть полезен.
Разрешения состояния и события $stateChangeStart
выполняются одновременно. Когда вы попытаетесь получить доступ к разрешенным данным в $stateChangeStart
, он будет недоступен, но он будет доступен, когда произойдет событие $stateChangeSuccess
.
Если вы используете $stateChangeStart
, вам нужно сделать checkAuth
из двух мест $stateChangeStart
события и основного разрешения. Поскольку они выполняют параллельное выполнение, по меньшей мере 2 сетевых запроса будут отправляться на сервер для тех же данных.
Вместо этого используйте $stateChangeSuccess
. Используя это, вы убедитесь, что ваши разрешения решены, и вы можете проверить доступ. Кроме того, вместо доступа к разрешенным свойствам доступ к разрешенным данным с помощью службы angular.