Angular Catch method not working with $http get request - angularjs

I am trying to neatly manage service function calls from the controller while handling errors from the API.
However when I do the following, I still get this is showing even with 403 and 404 errors in my console even when the API throws back a 403 or 404.
I am assuming this could work if I added catchin my services file but I would prefer keep this managed from the controller. Is this possible?
Controller:
angular.module('EnterDataCtrl', []).controller('EnterDataController', ['$scope', 'Data', '$state', function ($scope, Data, $state) {
vm = this;
vm.getRules = function (e, rule_query, data) {
if (e.keyCode == 13) {
Data.getRules(rule_query,data).then(function (data) {
console.log('this is showing even with 403 and 404 errors');
}).catch(function(res) {
console.log(res);
});
}
}
}]);
Service:
angular.module('EnterDataService', []).factory('Data', ['$http', function ($http) {
return {
getRules: function getRules(rule_query,data) {
var apiBase = apiUrl + 'get-rules';
var config = {
handleError:true,
params: {
rule_query: rule_query,
data : data
}
};
return $http.get(apiBase, config).catch(function () {});
}
}
}]);

In your service, you are setting up the catch without doing anything. This in general is bad practice, if you're going to use a catch block, then handle the error there. If not, don't declare the catch at all.
You can either remove the catch completely from the service method, which should result in your controller's catch handling it.
OR, you can leave the catch in the service call, and make sure to throw so that the error correctly bubbles up.

When a rejection handler omits a throw statement, it returns undefined. This converts a rejected promise to a fulfilled promise that resolves to undefined.
To avoid unintended conversions, it is important to include a throw statement in rejection handlers.
app.service('Data', ['$http', function ($http) {
this.getRules = function getRules(rule_query,data) {
var apiBase = apiUrl + 'get-rules';
var config = {
handleError:true,
params: {
rule_query: rule_query,
data : data
}
};
//return $http.get(apiBase, config).catch(function () {});
return $http.get(apiBase, config)
.catch(function (errorResponse) {
console.error("Data service error");
//IMPORTANT
throw errorResponse;
});
}
}]);

Related

Error handling in Angularjs for deferred promise

I built a factory to return data that uses an HTTP Get through a deferred promise. It work great when it is the happy path and the url is correct. But when there is an error I would like to catch it. It seems that I am but a 500 error still shows in the console. Is there a way to catch this also? Also, I want to do processing on the reject I'm having trouble figuring out how to do that. TIA
angular.module("accQueries")
.factory('leaseFactory', ['$http', '$q', function ($http, $q) {
return {
leases: '',
makeRequest: function (url) {
// Create the deferred object
var deferred = $q.defer();
$http.get(url).then(function (resp) {
deferred.resolve(resp.data);
})
// potentially catch http error here??
.catch(function (err) {
deferred.reject(err);
console.log('rejected : ' + err );
console.dir(err);
this.leases = '';
});
return deferred.promise;
},
// Return a single lease based on lease number
getLease: function (pLeaseNum) {
this.leases = this.makeRequest("http://someserver/AccruentQA_DB/webresources/restfulservices.latbllease/leaseNumber/" + pLeaseNum);
// Return the lease object stored on the service
return this.leases;
},
// Return all leases based on lease name
getLeases: function () {
this.leases = this.makeRequest("http://someserver/AccruentQA_DB/webresources/restfulservices.latbllease/name/");
// Return the lease object stored on the service
return this.leases;
}
};
}]);
It is not needed to wrap a $http call in $q, because $http returns a promise itself. So just returning $http like this is sufficient:
makeRequest: function (url) {
return $http.get(url);
}
If you would want to chain do something in the makeRequest function with the answers be4 passing it on, you can chain promises like so:
makeRequest: function (url) {
return $http.get(url).then(function(response){
//do something
return response;
}, function(error){
//do something
return error;
});
}
There's no way to prevent the HTTP error from appearing in the console. The browser does that before it passes the results back to angular. However, an error causes the $http promise to be rejected, which means you can handle it using the optional second argument to then()
return $http.get('url').then(
function(response) {
this.leases = response.data;
},
function(response) {
var statusCode = response.status;
var response = response.data;
// other error processing
this.leases = '';
}
}).then(function() { return this.leases; }
You can do various things depending on the status code and response data. If your server emits an error 500, that's what response.status will be. Timeouts have a status of 0.
You should also be aware that getLease() will return before the ajax request is complete. You should return the promise, and then in the calling code, chain another then() to do something once the promise is resolved.

Angular JS: Service Response not Saving to Variable

I'm trying to work out why the response of this service isn't saving to $scope.counter. I've added a function to my service fetchCustomers(p) which takes some parameters and returns a number, which I'd like to save to $scope.counter.
service
angular.module('app')
.factory('MyService', MyService)
function MyService($http) {
var url = 'URL'';
return {
fetchTotal: function(p) {
return $http.get(url, { params: p })
.then(function(response) {
return response.data.meta.total;
}, function(error) {
console.log("error occured");
})
}
}
}
controller
$scope.counter = MyService.fetchTotal(params).then(function(response) {
console.log(response);
return response;
});
For some reason though, this isn't working. It's console logging the value, but not saving it to $scope.counter. Any ideas?
If I understand your question correctly, you're setting $scope.counter to a promise, not the response.
MyService.fetchTotal(params).then(function(response) {
// Anything dealing with data from the server
// must be put inside this callback
$scope.counter = response;
console.log($scope.counter); // Data from server
});
// Don't do this
console.log($scope.counter); // Undefined

Request Interceptor to enforce Catch block in response

Here is my service's response:
response = response.then(function (data) {
return data.data;
});
response.catch(function (data) {
$q.reject(data);
});
// Return the promise to the controller
return response;
In Interceptor I am returning:
return $q.reject();
But, still I am getting back into:
response.then
Is possible to get back into the catch block?
Thanks
Adding more code:
.service('APIInterceptor', function ($q, $rootScope, UserService) {
var service = this;
service.request = function(config) {
return $q.reject();
//return config;
};
service.responseError = function (response) {
return response;
};
})
What happens is that your .request creates an error (by doing return $q.reject()), but your .responseError "handles" that error (by virtue of being there), thus resulting in the overall successful resolution.
Indeed, removing .responseError handler makes the error bubble up to .catch. Alternatively, you can also return $q.reject() in .responseError.

Have multiple calls wait on the same promise in Angular

I have multiple controllers on a page that use the same service, for the sake of example we will call the service USER.
The first time the USER.getUser() is called it does an $http request to GET data on the user. After the call is completed it stores the data in USER.data. If another call is made to USER.getUser() it checks if there is data in USER.data and if there is data it returns that instead of making the call.
My problem is that the calls to USER.getUser() happen so quickly that USER.data does not have any data so it fires the $http call again.
Here is what I have for the user factory right now:
.factory("user", function($http, $q){
return {
getUser: function(){
var that = this;
var deferred = $q.defer();
if(that.data){
deferred.resolve(that.data);
} else {
$http.get("/my/url")
.success(function(res){
that.data = res;
deferred.resolve(that.data);
});
}
return deferred.promise;
}
}
});
I hope my question makes sense. Any help would be much appreciated.
Does this work for you?
.factory("user", function($http, $q) {
var userPromise;
return {
getUser: function () {
if (userPromise) {
return userPromise;
}
userPromise = $http
.get("/my/url")
.then(function(res) {
return res.data;
});
return userPromise;
}
}
})
$http already returns the promise for you, even more, in 2 useful types: success & error. So basically, the option that #kfis offered does NOT catch errors and not flexible.
You could write something simple:
.factory('user', function($http) {
return {
getUser: function() {
$http.get('/my/url/')
.success(function(data) {
return data;
})
.error(function(err) {
// error handler
})
}
}
})

How do you pass more meaningful error information to $routeChangeError?

I have incredibly meaningful and powerful error messages that my server passes to the browser if there was any kind of error. But how do I access that info in the following:
$rootScope.$on("$routeChangeError", function (event, current, previous, rejection) {
console.log(rejection); // "An unknown error has occurred."
});
I'm using $routeProvides.resolve in my route definitions, and so $routeChangeError was going to be my way of handling if those promises didn't resolve. Is there for a way for me to access the response from the server and display that somehow?
Thanks!
Each property on a resolve object should be a function that returns a promise. So if one of your routes doesn't resolve, throwing a reason in the .catch handler will pass your error information to the $routeChangeError handler
$routeProvider
.when("/foo", {
templateUrl: "foo.html",
controller: "fooController",
resolve: {
message: function($http){
return $http.get('/someUrl').
then(function(response) {
return response.data;
}).
catch(function(response) {
throw response.data;
});
}
}
});
Assuming the data parameter has the data from the server you want to use, this will end up in the rejection parameter on the $routeChangeError event.
You dont need to do this in this way, you can do this in your Controller:
In some service:
angular.module('app').service('SomeService', function($http) {
this.getData = function() {
return $http.get('url');
}
})
.controller('MainCtrl', function(SomeService) {
SomeService.getData().then(function(response) {
//the promise is resolved
// response is the response from server
}).catch(function(error) {
console.log(error) // this error is from your server if the promise rejected
// Then you can add {{error}} on your html and add here
$scope.error = error.data;
});
});

Resources