Angular View не обновляется после http-перехватчика
Я использую обещание в контроллере, чтобы сделать представление после $http
запрос. Все работает хорошо, за исключением случаев, когда перехватчик http включается во время ошибок 401. Когда это происходит, вот проблема, с которой я сталкиваюсь:
Перехватчик http обнаруживает ошибки 401 из API, добавляет отклоненный запрос в буфер и показывает модальный вход.
После успешного входа в систему запрашиваемые API-запросы в очереди, которые ранее были 401, повторяются, и API успешно возвращает запрошенные данные.
Однако, что происходит, даже когда данные возвращаются из API, угловой вид не обновляется. Представление обновляется только после обновления этого состояния вручную.
Вот мои коды:
1) http перехватчик:
я использую http-auth-interceptor
в этом
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
if (!rejection.config.ignoreAuthModule) {
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function(value, key) {
if(rejection.data.error === value) {
var deferred = $q.defer();
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
}
})
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
2) http перехватчик смотреть:
scope.$on('event:auth-loginRequired', function() {
LoginModalService.openLoginModal();
console.log('login needed');
});
scope.$on('event:auth-loginCancelled', function() {
$state.go('index');
});
3) Контроллер:
.controller('FetchData', ['$scope', 'DataService',
function($scope, DataService) {
DataService.fetchNamesList()
.success(function(names) {
$scope.namesList = names;
}).error(function(error) {
// The login modal should show if auth error occurs
});
}])
Здесь DataService.fetchNamesList()
делает запрос на получение от API.
Если для вышеуказанного срабатывает аутентификационный перехватчик, я вижу $http $_GET
повторяется после успешного входа в систему, но $scope.namesList
не обновляется в поле зрения.
Что я думал до сих пор:
1) Я думал о добавлении дополнительного $watch
для вышеупомянутого для того, чтобы это работало после перехватчика http. Я подумал об этом, тогда я отказался от этой опции, так как, если у меня есть несколько запросов на одну страницу (например, на панели инструментов), то просмотр каждого запроса может оказаться за бортом.
2) Вручную обновите состояние маршрутизатора пользовательского интерфейса после входа в систему, как показано ниже. Это работает, но это не помогает, когда представление включает отправку формы, где заполненная форма будет сброшена после перезагрузки состояния:
$state.go($state.current, {}, {reload: true});
Поэтому я уверен, как решить эту проблему. Может кто-нибудь направить меня сюда, пожалуйста....
РЕДАКТИРОВАТЬ: После предложения @ChrisFoster в комментариях я переместил return deferred.promise
вне angular.forEach
и немного изменил проверку foreach. Кажется, это корректно обновляет вид после входа в систему. Тем не менее, теперь модальные показы для всех ошибок, а не только те, что перечислены в rejectionReasons:
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
if (!rejection.config.ignoreAuthModule) {
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
// Loop through each rejection reason and redirect
angular.forEach(rejectionReasons, function(value, key) {
if(!(rejection.data.error === value)) {
return $q.reject(rejection);
}
});
var deferred = $q.defer();
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
2 ответа
Рад, что ваш ответ работает. Вы все еще можете использовать angular.forEach
Если хотите, вот пример того, как это будет выглядеть:
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
var loginRequired = false;
var rejectionReasons = [
'token_not_provided', 'token_expired',
'token_absent', 'token_invalid'
];
// Loop through each rejection reason
angular.forEach(rejectionReasons, function(value, key) {
if (!(rejection.data.error === value)) {
// Set loginRequired, and check this later
// We *can't* return here because we are inside a function :)
loginRequired = true;
}
});
// Create a promise
var deferred = $q.defer();
if (loginRequired && !rejection.config.ignoreAuthModule) {
// Display a login module and queue a retry request
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
} else {
// Not a auth error, or ignoreAuthModule is enabled
// Therefore immediately reject the promise with the fail value
deferred.reject(rejection);
}
// Return the promise
return deferred.promise;
}
};
}]);
Причина, по которой вы столкнулись с проблемами forEach
потому что вы передаете функцию в качестве аргумента forEach
, Когда ты return
внутри вы возвращаете это значение в forEach
функция, а не в функции перехватчика HTTP. Поэтому у вас есть три варианта:
- Вы могли бы использовать традиционный JavaScript
for
петля - Вы могли бы использовать
if
заявление условное - Вы можете использовать "внешнюю" переменную, которую вы отслеживаете
Ваш пример использует второй подход, но если вы все еще хотите использовать angular.forEach
мой код выше является демонстрацией третьего подхода. Первый подход также возможен, но немного менее распространен и немного более грязен.
Это то, что я использую в данный момент, который работает:
1) Удален angular.forEach
и заменил это простым if
заявление. Так как я проверяю только несколько условий, if
не слишком плохая альтернатива, я думал.
2) я добавил deferred.promise
внутри if
заявление и после этого все работало нормально. Представление обновлялось после входа в систему, и у контроллера не было проблем с продолжением разрешения данных, возвращаемых из API.
Это мой код:
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer',
function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
if (!rejection.config.ignoreAuthModule) {
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
// Loop through each rejection reason and redirect
if (
rejection.data.error === 'token_not_provided' ||
rejection.data.error === 'token_expired' ||
rejection.data.error === 'token_absent' ||
rejection.data.error === 'token_invalid'
)
{
var deferred = $q.defer();
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
}
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
Спасибо @ChrisFoster, который указал на ошибку:
Ваше возвращение deferred.promise находится внутри angular.forEach. Это означает, что он возвращается внутри этой функции, а не в обработчик обещаний. Вам нужно будет присвоить это переменной за пределами функции forEach и вернуть таким образом.