Отложить Angular UI Router $stateChangeStart до получения ответа на авторизацию сервера
У меня есть приложение Angular, использующее UI-маршрутизатор, где я пытаюсь проверить токен пользователя, если он существует, когда приложение запускается. Я также проверяю, что у пользователя есть разрешение на доступ к сертификационным маршрутам. Проблема в том, что $stateChangeStart
выполняется до того, как я получу ответ от конечной точки авторизации. Здесь некоторый код (coffeescript с js ниже) - это все в моем блоке run
.
app.run(($rootScope, $state, $stateParams, $log, Auth) ->
currentState = 'home'
$rootScope.$state = $state
# read a cookie if cookie exists
if Auth.setAuthenticationToken()
# hit api endpoint to validate token
Auth.validateToken (user) ->
# route to current state
# this returns after $stateChangeStart runs below
$state.go(currentState)
$rootScope.$on '$stateChangeStart', (event, toState, toParams, fromState, fromParams) ->
currentState = toState.name
Auth.setAuthenticationToken()
$rootScope.error = null
# compare users access permissions with incoming route access level
if (!Auth.authorize toState.data.access, Auth.user)
event.preventDefault()
$rootScope.error = "Sorry, you haven't provided the required credentials."
$log.warn $rootScope.error
)
Мой вопрос в том, как я могу запустить $stateChangeStart
только после того, как ответ от конечной точки auth был возвращен. Это нужно только в первый раз. Каждое последующее изменение состояния может быть выполнено без попадания конечной точки auth.
Ответы
Ответ 1
Я бы создал функцию в вашей службе Auth
, которая возвращает обещание. Позже, разрешите (разрешить) или отклоните (не authrized), который отложен. Затем используйте его в свойстве resolve
определений маршрутов
$stateProvider.state('stateName',{
...
...
resolve: {
isAuthorized: function(Auth){
return Auth.checkAuthorization();
}
}
})
Для поддержки последующих проверок вы можете сохранить обещание в сервисе. Это может выглядеть так:
myApp.service('Auth',function($http,$q){
var authStatusDeferred = $q.defer();
this.checkAuthorization = function(){
return authStatusDeferred.promise;
};
this.validateToken = function(user){
var isValid = false;
//..do validation stuff
if(isValid) authStatusDeferred.resolve();
//Otherwise state change will not happen..
};
});
о, жаль, что кофе нет
Ответ 2
Вы должны позвонить $urlRouter.sync()
после получения пользовательских данных. Также вы можете защитить $stateChangeStart
, используя event.preventDefault()
, если пользователь не был получен.
Ответ 3
Я думаю, что слишком поздно для ответа на этот вопрос, но я хочу прокомментировать здесь кого-то, кому может понадобиться небольшая идея.
Я пытался сделать почти то же самое в течение долгого времени, и я прекратил это делать. Я не думаю, что слушатель stateChangeStart
является надежным в использовании.
Я думаю, что лучший способ сделать то, что вы хотите, украсить $state.go
с помощью службы $provide
. Вот пример:
/* config your angular app */
.config(function($provide) {
$provide.decorator('$state', function($delegate) {
var myState = $delegate;
// Rename original 'go' method
myState.originalGo = myState.go;
myState.go = function(toState, toParams, options) {
/*
* Do what you want to do :)
*/
this.originalGo(toState, toParams, options);
};
return $delegate;
});
При таком подходе вы можете делать все, что хотите, до $stateChangeStart
.
Надеюсь, это поможет.
Ответ 4
Я нашел очень хороший способ сделать эту работу должным образом и не полагаться на решение вообще в другом ответе здесь. Вот код:
rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState) {
if (dataService.isInitialized()) {
proceedAsUsual(); // Do the required actions based on the data you received in the service
}
else {
event.preventDefault();
dataService.intialize().success(function () {
$state.go(toState, toParams);
});
}
});
Тогда вы можете просто помнить, что ваши данные уже инициализированы в службе так, как вам нравится, например:
function dataService() {
var initialized = false;
return {
initialize: initialize,
isInitialized: isInitialized
}
function intialize() {
return $http.get(...)
.success(function(response) {
initialized=true;
});
}
function isInitialized() {
return initialized;
}
};