Reputation: 1159
I can understand how to make $http request interceptor with retry logic like in this question
But I cannot figure out how to make a modal popup(angular-ui - bootstrap) with retry button which get poped up every time error happens, even repeatedly. Can anyone drop me some advise?
Upvotes: 1
Views: 2113
Reputation: 27012
There are a number of components to this, and you need to know about chaining promises to understand how it all fits together. First the interceptor
app.factory('RetryInterceptor', function($injector, $timeout, $q) {
return {
'responseError': function(rejection) {
// Avoid circular dependency issues
var Retry = $injector.get('Retry');
var $http = $injector.get('$http');
// Timeout is just to keep UI from changing too quickly
return $timeout(angular.noop,1000).then(function() {
return Retry.show();
}).then(function() {
return $http(rejection.config);
}, function() {
return $q.reject(rejection);
});
}
}
});
registered as
app.config(function($httpProvider) {
$httpProvider.interceptors.push('RetryInterceptor');
});
The above responseError interceptor returns a promise that is resolved/rejected with the returned promise of Retry.open
. If that promise is resolved, then it will retry the original $http
request. If it's rejected, then nothing will happen, other than the promise returned from the original call to $http
will be rejected, with the same rejection object it would have had without the dialog being shown.
Retry
is a custom service, that exposes a single method, open
. You could have this in the interceptor, but this keeps things fairly modular:
app.service('Retry', function Retry($window, $modal) {
this.show = function() {
return $modal.open({
templateUrl: 'retry-dialog.html',
controller: 'RetryController'
}).result;
}
});
The show
method returns a promise from the $modal
service. This promise is controlled by the $modalInstance
object passed to the controller:
app.controller('RetryController', function($scope, $modalInstance) {
$scope.retry = function() {
// Will resolve the promise
$modalInstance.close();
};
$scope.cancel = function() {
// Will reject the promise
$modalInstance.dismiss();
}
});
And the template for the modal can be something like:
<div class="modal-header">
<h3>Error!</h3>
</div>
<div class="modal-body">
<p>Something went wrong!</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="retry()">Retry</button>
<button class="btn btn-danger" ng-click="cancel()">Cancel</button>
</div>
which will then resolve the promise if "Retry" is clicked, or reject the promise if "Cancel" is clicked.
You can see all this working in this Plunker.
A few extra points:
You could pass details of the original failure using the resolve
option to $modal.open
, so you can customise the message shown.
You might not want to show the dialog for all requests. If there is an error fetching the template for the modal, for example, you'll end up with an infinite loop. You could test for the http status code to only show the dialog for certain failures, or another way of controlling this is to add custom options to the config
object you pass to $http
, which you can then test for in the responseError
interceptor, to determine how the failure is handled.
Upvotes: 4
Reputation: 1240
Look at this Angular module http://ngmodules.org/modules/http-auth-interceptor It is made to intercept non-authorized requests and retry them. You can easily adapt this module for your needs. Replace if (rejection.status === 401 && !rejection.config.ignoreAuthModule)
with your logic. Bind to 'event:auth-loginConfirmed'
event with $rootScope.$on('event:auth-loginConfirmed', ...)
at app.run()
to open your modal. In your modal inject authService
factory and make a call to authService.loginConfirmed()
this will retry request with your error logic
Upvotes: 0