I am trying to get http error if service failed to load a url. I have created a angular factory which is like this:
loadUsers: function () {
return $http.get(urlService.url("/users"));
},
in controller i try to using this factory method to load ruserlist:
urlservice.loadUsers()
.then(function(response) {
$log.info("user loaded");
})
.finally(data.bind(undefined, result));
at this point i want to handle http error but not getting idea where i have to use error function as this is returning a promise. Can someone give me hint.
Just add a .catch to your promise:
urlservice.loadUsers()
.then(function(response) {
$log.info("user loaded");
})
.catch(function(err) {
console.log(err);
})
.finally(data.bind(undefined, result));
add a second callback to the .thenmethod, that will be triggered in case of error.
from the angular doc:
https://docs.angularjs.org/api/ng/service/$http
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Just add another function inside promise like this
urlservice.loadUsers()
.then(function(response) {
$log.info("user loaded");
},function(response) {
$log.info("error");
})
.finally(data.bind(undefined, result));
urlservice.loadUsers().then(successCallback, errorCallback)
.finally(data.bind(undefined, result));
var successCallback = function(response) {
// handle data recieved
$log.info("user loaded");
};
// create generic method for handling service errors
var errorCallback = function(error) {
// handle error here
$log.info("error occurred");
};
Related
$scope.observer_vel_data = function(){
$scope.showOverlay('loadRefPubVel');
$http({
//First http post request
method:'POST',
url:'/api/observer_vel_data',
data:$scope.payload_array,
}).then(function successCallback(response){
console.log('API Endpoint: vel data success!');
//Second post request is made in the method call below
$scope.sentiment_var = $scope.observer_send_sentiment();
$scope.vel_var = response.data.velocity1;
}, function errorCallback(response){
// console.log(response);
$scope.addAlert({
type: 'danger',
msg: 'API call failed'
});
}).finally(function(){
console.log("hello");
console.log($scope.sentiment_var);
//graph is rendered
$scope.update_velocity($scope.vel_var,$scope.sentiment_var);
$scope.hideOverlay('loadRefPubVel');
});
};
So I am trying to render a graph which uses data from two different and independent post requests. However, the graph command is called before the data from the second post request arrives. How can I fix this ? The commands which make the post requests and render the graph are mentioned as comments in the code posted.
$scope.observer_send_sentiment = function (){
// $scope.showOverlay('loadRefSentiment');
var data = {
"angularGroups":$scope.groups
};
// console.log(data);
$http({
method:'POST',
url:'http://localhost:9612/sentiment_velocity',
data:data
}).then(function successCallback(response){
var data = response.data;
var json_obj = JSON.parse(data.replace(/\'/g,'"'));
var sentiments = json_obj["sentiments"];
// console.log(sentiments);
$scope.update_sentiment(sentiments);
console.log(sentiments);
return sentiments;
}, function errorCallback(response){
var errmsg = response.statusText;
console.log(response);
$scope.addAlert({
type: 'danger',
msg: 'API call failed (sentiment basic)' + errmsg,
});
}).finally(function(){
// $scope.hideOverlay('loadRefSentiment');
});
};
If I understand correctly, you want the code in finally(...) execute only after the second request ends.
To enforce that, you need to chain the HTTP request promises, meaning you need to return the promise of the second HTTP request from the success handler of the first request. Your code should look like more or less like this:
$scope.observer_vel_data = function(){
$scope.showOverlay('loadRefPubVel');
$http({
method:'POST',
url:'/api/observer_vel_data',
data:$scope.payload_array,
}).then(function successCallback(response){
console.log('API Endpoint: vel data success!');
$scope.vel_var = response.data.velocity1;
return $scope.observer_send_sentiment();
}).catch(function errorCallback(response) {
//This catch will handle errors from both HTTP requests
$scope.addAlert({
type: 'danger',
msg: 'API call failed'
});
})
.finally(function(){
console.log("hello");
console.log($scope.sentiment_var);
//graph is rendered
$scope.update_velocity($scope.vel_var,$scope.sentiment_var);
$scope.hideOverlay('loadRefPubVel');
});
};
$scope.observer_send_sentiment = function() {
return $http({...}).then(function(response) {
//process the response
...
$scope.sentiment_var = parsedResponseData;
});
};
Note that the finally callback will always execute, regardless of whether an error occured or not. If you want some of it to execute only if an error was not encountered, add another .then(function() {...}) instead.
EDIT: Now that we can that see what observer_send_sentiment does, it might make sense for you to stick to the .then(function successCallback() {...}, function errorCallback() {...}) syntax, and keep separate error callbacks for each of the requests. Just keep in mind that if you ever add another then block and you want errors along the promise chain to prevent executing further .then(function() {...}) blocks, you should add return $q.reject(response) at the end of both errorCallbacks. Not using q.reject from within error callbacks with the .then(function successCallback() {...}, function errorCallback() {...}) syntax renders the promise resolved, not rejected.
Having an angular service that returns promise, is it possible to detect whether a consumer of this promise handles error ? I'd like to provide a default error handling in service, but ensure that it would be used only if no error handler is defined down the execution chain.
The service method looks like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
console.log('ERROR!'); // default error handling
}
);
}
The serviceMethod returns a promise, therefore:
1) If the consumer provides error handler, the error should be handled exclusively by it.
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
// Custom error handling.
});
}
2) If the consumer doesn't provide handler, the error should be handled exclusively by service handler.
Is it possible to achieve in the first successor of serviceMethod? Is it possible at any point in the chain (the error is handled exclusively by the first consumer to provide error handler)?
You have the answer in the code you haven given. Do it like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
return response; // default error handling
}
);
}
And your getResponse method:
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
alert(error.code); //Default error handling returned from error function in serviceMethod
alert("My custom error"); //Custom error handling
});
}
It is very important that the rejection handler in the service throw the error response. Otherwise the $q service will convert the rejected promise to a successful response.
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (errorResponse) {
//return response; // default error handling
throw errorResponse;
//OR
//return $q.reject(errorResponse);
}
);
}
A common problem is erroneous conversion of rejected promises to fulfilled promises by failing to return anything. When a function omits a return statement, the function returns a value of undefined. In that case the $q service will convert a rejected promise to a fulfilled promise that resolves with a value of undefined.
That said. No, it is not possible for a service to know how a consumer will use a rejected promise. If a consumer wants a service to skip default error handling, the consumer must specify that in the service call:
function serviceMethod(method, url, data, skipErrorHandling)
{
return $http({
method: method,
url: url,
data: data
})
.then(function (response) {
return response.data;
})
.catch(function (errorResponse) {
if (skipErrorHandling)
throw errorResponse;
}
//Put error handler here
//Correct error
var promise = retry(method, url, data);
return promise;
);
}
So I have pulled the interceptor straight from the angular HTTP documentation and yet this still doesn't work. The "request" and "response" functions get called ,but never the "requestError" or the "responseError".
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'request': function (config) {
return config; //gets called
},
'requestError': function (rejection) {
return $q.reject(rejection); //Never gets called
},
'response': function (response) {
return response; //gets called
},
'responseError': function (rejection) {
return $q.reject(rejection); //Never gets called
}
};
});
}]);
On the server I am returning a 400, but really any error would do. And here is the service
User.publicProfileGetProfile = function (value, type) {
return $http({
url: '/public/profile/' + type + '/' + value,
method: 'GET'
}).then(function (response) {
return response;
}, function(error){
return error;
});
};
No error functions are being called and every response goes through the response function. The standard angular error is displayed with the Bad Request (400) as usual. When the 400 error is returned, it is simply 'undefined' through the 'response' function in the interceptor.
Let me know if I've forgotten to include any important information.
By using return, the error handler is converting the rejection to a success. Instead use throw to chain the rejection.
User.publicProfileGetProfile = function (value, type) {
return $http({
url: '/public/profile/' + type + '/' + value,
method: 'GET'
}).then(function onSuccess(response) {
return response;
}, function onReject(error){
//return converts rejection to success
//return error;
//use throw to chain rejection
throw error;
});
};
When I saw that the JSFiddle (from #georgeawg) was working properly, I made sure mine looked exactly the same. When it didn't work, I looked around to see if I had any other interceptors that might cause problems. I had another interceptor that was being hit first and returning any errors as responses, then they would go through this one and it would process it as a successful response. I removed it and everything seems to be working correct now!
I'm trying to play around with RxJS and Angular 1. I'm running into an issue where I can't call .done(), .fail() or .always() on an $http request. Is it possible to use RxJS with the $http service in Angular 1?
This is how I have my observable set up so far:
angular.module('rxExampleApp')
.controller('Step3Ctrl', function ($http) {
var vm = this;
vm.requestStream = Rx.Observable.just('https://api.github.com/users');
vm.requestStream.subscribe(function (requestUrl) {
// execute the request
vm.responseStream = Rx.Observable.create(function (observer) {
$http.get(requestUrl)
.done(function (response) {
observer.onNext(response);
})
.fail(function (jqXHR, status, error) {
observer.onError(error);
})
.always(function () {
observer.onCompleted();
});
});
});
vm.responseStream.subscribe(function (response) {
console.log('TEST RESPONSE: ', response);
});
});
But I'm running into an error where $http.get.done is not a function. Is what I am trying to accomplish possible in Angular 1.4 with RxJS?
done, fail & always these methods are available there on jQuery Ajax, not on angular $http method's.
$http.get doesn return a promise, and you could have .then function over it. So you could place success, error & completed callback.
Code
$http.get(requestUrl)
.then(function (response) { //success function
observer.onNext(response);
}, function (error) { //error function
observer.onError(error);
}, function () { //completed callback
observer.onCompleted();
}
);
I added in my application a "loading screen". As found in this post: 'Click'
Now I have the problem that all $http request results in the "success" callback. Even when the url does not exist.
$http.post("this doesnt even exist", { })
.success(function (data, status, headers, config) {
alert("success"); //this callback is called
})
.error(function (data, status, headers, config) {
alert("error"); //never called
});
When I disable the 'responseInterceptor' everything works fine. (exception, not found, wrong parameters -> all results in error callback)
I'm using a .NET Webservice to get my data from.
The values of parameters in success callback
data: ''
status: 0
headers: 'function(name) { ... }'
config: JSON.stringify(config) '{"method":"POST","url":"this doesnt even exist","data":{}}'
It's because the response interceptor you linked to "swallows" the error.
In the following code:
return promise.then(hide, hide);
The first hide is for the success callback, while the second is for the error callback.
The hide function itself just ends with return r; in all cases, which means it will return the response.
For your $http.post to know there was an error the response interceptor needs to return the promise as rejected instead: return $q.reject(reason);
Something along these lines should hopefully work or at least give further guidance (note that I have not been able to test it):
$httpProvider.responseInterceptors.push(function ($q) {
return function (promise) {
numLoadings++;
loadingScreen.show();
var hide = function () {
if (!(--numLoadings)) loadingScreen.hide();
};
var success = function (response) {
hide();
return response;
};
var error = function (reason) {
hide();
return $q.reject(reason);
};
return promise.then(success, error);
};
});