AngularJS ngResource: How to always use the same catch function? - angularjs

Every time I hit the servers using any $resource I want to show the same alert to my users whenever it fails.
Today, it looks like:
function tryAgain() { alert("try again") }
myResource.query().$promise.catch(tryAgain);
myResource.update(...).$promise.catch(tryAgain);
myResource.delete(...).$promise.catch(tryAgain);
otherResource.query().$promise.catch(tryAgain);
Is there a way to configure the default error handling function for ngResource? I'm looking for something like:
$resource.somethingMagicHere.defaults.catch(tryAgain);

You can use an interceptor in your app.config() section. This will catch all response errors originating from $http which $resource uses.
$httpProvider.interceptors.push(function($q) {
return {
'responseError': function(response) {
if (response.status == 401) {
// Handle 401 error code
}
if (response.status == 500) {
// Handle 500 error code
}
// Always reject (or resolve) the deferred you're given
return $q.reject(response);
}
};
});

The #kba answer helped me find the path. The following article made me understand it:
http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/

Just declare it once and reuse:
var defaultErrorHandler = function() {
alert("try again")
}
myResource.query(...).$promise.catch(defaultErrorHandler());
myResource.update(...).$promise.catch(defaultErrorHandler());
...

Related

How do i manually fail an angular $http requests in an interceptor

So i have a server that is returning 200 as a response every time. I need to check the response for an error response.data.error If i find an error i need to send the http request to the catch methods in my application code.
Here is an example of what i want to happen..
interceptor code
// interceptor code
{
response:function(response){
if (response.data.error) {
return $q.reject(response)
}else{
return response
}
},
responseError:function(rejection){
// error handling logic
return $q.reject(rejection)
}
}
app code
// application code
$http.get('/api/stuff')
.then(function(){
// do success stuff
}).catch(function(){
// the function i want to run!!!
})
Try catching the error by:
$http.get('/api/stuff')
.then(function(data){
console.log(data); // do success stuff
}, function(error){
console.log(error); // will catch your error I hope
}
I dont think that .catch will cacth it, you need to preform the check in the .then block.
Ok it was more of a silly mistake. Here is what i did wrong.
like a dumb i returned inside of a function instead of directly inside of the response. so what i ended up doing is creating a flag and if that flag was true then return directly on the response function.
interceptor code
// interceptor code
{
response:function(response){
myCustomErrorFunction(){
if (response.data.error) {
return $q.reject(response)
}else{
return response
}
}
},
responseError:function(rejection){
// error handling logic
return $q.reject(rejection)
}
}
app code
// application code
$http.get('/api/stuff')
.then(function(){
// do success stuff
}).catch(function(){
// the function i want to run!!!
})
Thanks for the help guys.

ERR_CONNECTION_REFUSED calls my $http sucess function

Running my Angular app without a server running should return an error, or course. But this "ERR_CONNECTION_REFUSED" I see in my Chrome's console triggers my success function of my membership service:
function login(user, onSuccess, onError){
$http.post(_loginAddress, user)
.success(loginSuccess)
.success(onSuccess)
.error(loginError)
.error(onError);
}
function loginSuccess(data) {
// this code executes with data = null here.
if (data.ok === true) {
...
}
}
function loginError(data, code) {
...
}
The relevant section of the $http docs states:
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the error callback will not be called for such responses.
Am I suppose to program my $http success() functions for possible false-positive?
EDIT
So .. I went hunting and found that one of my interceptors (authentication) was causing this ERR_CONNECTION_REFUSED error response to be considered as 'recovered' and therefore the success function was called.
I changed this:
if (response.status !== 401) {
return response;
}
To this:
if (response.status !== 401) {
return $q.reject(response);
}
And everything is fine again.
Hope this might help someone else.

catching 403 errors in angular and showing a pop up

On our site, I get into this predicament where you basically transfer a project over to another user. When that happens, if the original user tries to view the project he/she just transferred, we give a 403 back because they no longer are the owner of the project. I started to look up interceptors in angular. I hooked up the responseError to just see if it gets called on the 403 like so
.config(($httpProvider) => {
$httpProvider.interceptors.push(function() {
return {
responseError: function(rejection) {
console.log("bad response");
return rejection;
}
}
});
});
So my "bad response" gets called and everything, but I was not sure how I could show a modal view or something at this point that shows the error to the user since this 403 response actually happens on a few of our different resources, not just projects. Any thoughts? Thanks in advance.
If i understand correctly you want to show the error dialog only for some http calls not every call that goes through you interceptor. You could probably try this:-
Set a config for your http calls say handleError:true.
$http.get('myurl', {handleError:true})....
$http.post('myurl',data, {handleError:true})....
etc..
and in your interceptor look for that specific config setting to display the error:-
$httpProvider.interceptors.push(function() {
return {
responseError: function(rejection) {
console.log("bad response");
if(rejection.config.handleError && rejection.status === 403){
//show error dialog
}
return rejection;
}
}
});
Also you could possibly send the status code that needs to be handled as well.
$http.get('myurl', {handleStatus:[403,...]})....
and
$httpProvider.interceptors.push(function() {
return {
responseError: function(rejection) {
if((rejection.config.handleStatus || []).indexOf(rejection.status) > -1){
//show error dialog. probably you could show it in a $timeout to make this async.
}
return rejection;
}
}
});

AngularJS ngResource $save feedback

Hello I am using ngResource $save method and I get two different behaviours, I don't understand why
First I'm using it in this way:
$scope.user = new User($scope.user);
$scope.user.$save(function () {
$window.location.href = //redirection here;
}, function (response) {
$scope.form.addErrors(response.data.errors);
});
Then I have another controller when I'm doing a similar operation, but even getting 404 or 422 errors from the server the first callback is executed and the errors callback is ignored.
Does anyone have any idea of this? I've been searching in Google for hours trying to find more documentation about $save but I'm still stuck with this problem.
Thank you.
Well, the problem was on an interceptor I am using to detect 401 (unauthorized errors)
here is the interceptor, notice that you must return $q.reject(response) otherwise the other callbacks are not called (in my case the error callback in ngResource.$save)
MyApp.config(function ($httpProvider) {
$httpProvider.interceptors.push(function($window, $q) {
return {
'responseError': function(response) {
if (response.status == 401) { // Unathorized
$window.location.href = 'index.html';
}
// return response; <-- I was doing this before cancelling all errors
return $q.reject(response);
}
};
});
});

Reject from 'response' into 'responseError'

Sometimes, the API I'm using will return 200 ok even though there has been an error. The response JSON object will look something like:
{
error: true
}
I've built a $http response interceptor that simply checks for this error and rejects it. I want it to then jump into my responseError function:
$httpProvider.interceptors.push(function($q) {
return {
response: function (response) {
if (response.data.error) {
// There has been an error, reject this response
return $q.reject(response);
}
return response;
},
responseError: function(rejection) {
// Display an error message to the user
...
return $q.reject(rejection);
}
}
});
Problem is, even after rejecting the response, my responseError function isn't called. It is called on 500 errors etc so I know it's working. I'd expect a rejection to do the same thing.
From the docs:
responseError: interceptor gets called when a previous interceptor threw an error or resolved with a rejection.
Any ideas on what's missing?
Looks like this isn't possible to do. To cut down on duplicate code, simply declare the error handling function separately and reuse it inside the response and responseError functions.
$httpProvider.interceptors.push(function($q) {
var handleError = function (rejection) { ... }
return {
response: function (response) {
if (response.data.error) {
return handleError(response);
}
return response;
},
responseError: handleError
}
});
To add to this answer: rejecting the promise in the response interceptor DOES do something.
Although one would expect it to call the responseError in first glance, this would not make a lot of sense: the request is fulfilled with succes.
But rejecting it in the response interceptor will make the caller of the promise go into error handling.
So when doing this
$http.get('some_url')
.then(succes)
.catch(err)
Rejecting the promise will call the catch function. So you don't have you proper generic error handling, but your promise IS rejected, and that's useful :-)
Should you want to pass the http response to the responseError handler, you could do it like this:
$httpProvider.interceptors.push(function($q) {
var self = {
response: function (response) {
if (response.data.error) {
return self.responseError(response);
}
return response;
},
responseError: function(response) {
// ... do things with the response
}
}
return self;
});

Resources