How can I retrieve HTTP status codes using Angular's $q promises? - javascript

I have within my code that makes an $http.post to a back end, and of course things can go wrong. If I use the below syntax within angular:
$http.post(url).success(function(data, status, headers, config))
.error(function(data, status, headers, config));
If there is an error, I can retrieve the error code using status and handle it within the function defined in error(). However, if instead I take this approach (which is the one I am in fact using):
var deferred = $q.defer();
$http.post(url).success(deferred.resolve).error(deferred.reject);
return deferred.promise;
With the second syntax, I have all of my ajax calls within a separate ServiceJS directory, and can handle the successes or errors on a case by case basis. For instance, if the second snippet was Service.MyFunction() then in the code where I need it I would:
Service.MyFunction().then(function() {},
function(data, status, headers, config) {});
However, if I use this code block, status, headers, config are all undefined. My question is how can I retain this syntax but still retrieve the error code?
As an added reference, the back end is a C# Web API project, which would return errors using return BadRequest(); or the like.

Try something like this:
myFunction(){
var deferred = $q.defer();
// you can use .then() instead of .success or .error
$http.post(url).then(function(successResponse){
var data = successResponse.data;
var status = successResponse.status;
...
deferred.resolve(successResponse);
}, function(failureResponse){
var status = failureResponse.status;
var config = failureResponse.config;
...
deferred.reject(failureResponse);
});
return deferred.promise;
}

well, I'd say it's a good practice and more standard to implement a http interceptor and handle the HTTP errors from there intead of handling the error one by one on each http or resource object, and your code will be located in a single location.
basically, you can segment the actions to take depending on the error status you get for example:
angular.module('app').factory('myInterceptor', ['$q',
function($q){
return {
'responseError': function(rejection){
switch(rejection.status){
case 0:
//'No connection, is the internet down?'
break;
case 422:
// error
break;
case 484:
// error
break;
default:
// message rejection.status, rejection.statusText
break;
}
return $q.reject(rejection);
}
};
}
]);

$http is already returning a promise, so why not use that one?
function myFunction() {
return $http.post(url);
}
// ...
myFunction().success(function(data, status, headers, config) {
// ...
});
... or ...
myFunction().then(...);
The promises returned from $http have the methods success and error in addition to the other promise methods. Read more at Angular docs for $http.

Related

(Angular)Js - how to simplify promise result?

I come from an embedded C background, and am self-taught in AngularJs 1.x; I suspect that my question is a generic JS question, rather than AngularJs specific.
I find this sort of pattern repeating in my code:
$http.get(url)
.success(function(data, status, headers, config)
{
console.log('Success');
});
})
.error(function(data, status, headers, config)
{
console.error('Error !');
});
Being a C guy, I am not similar with lambdas, although I am comfortable with callbacks.
The code for the success and failure parts can be very large, and it makes the code seem messy - especially seeing all that code passed as a parameter.
I am guessing that the parameters to $http is/are (a) promise(s).
Is there some way that I can make the code more modular, and easier to read & maintain?
For instance, I imagine that I could declare some success/failure functions & invoke those, stemming like:
function it_succded(data, status, headers, config))
{
console.log('Success');
});
function it_failed(data, status, headers, config)
{
console.error('Error !');
});
$http.get(url)
.success(function(data, status, headers, config)
{
it_succded(data, status, headers, config))
});
})
.error(function(data, status, headers, config)
{
it_failed(data, status, headers, config)
});
});
Of course, I could just code it & see, but ask here because I want to learn, and hope for an explanation from someone who truly understands this, preferably someone who codes (Angular)Js professionally.
For using the promise interface, you should not use .success() or .error(): they are deprecated. Instead, use .then() and catch(). The parameters these callbacks receive are slightly different:
$http.get(url).then(function(response) {
console.log("Success", response.data);
}).catch(function(response) {
console.log("Error", response.status);
});
response is an object that has the expected properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
xhrStatus – {string} – Status of the XMLHttpRequest (complete, error, timeout or abort).
You could indeed define the callback functions separately, and then your callback arguments can just be function references:
function it_succded(response) {
console.log("Success", response.data);
}
function it_failed(response) {
console.log("HTTP Error", response.status, response.statusText);
}
$http.get(url).then(it_succded).catch(it_failed);
You could define these functions as methods, e.g. on the $scope object:
$scope.it_succded = function (response) {
console.log("Success", response.data);
$scope.data = response.data;
}
$scope.it_failed = function (response) {
console.log("HTTP Error", response.status, response.statusText);
}
$http.get(url).then($scope.it_succded).catch($scope.it_failed);
Little note: Be aware that this will not be set when these callbacks are called by the Promise implementation. So either do not use this in them, or define them as arrow functions (this will then be whatever it is in the lexical context), bind the functions to a specific this object, or provide little wrapper callbacks:
.then(function(response) {
return $scope.it_succded(response);
})
Yes, you can.
$http({
your configs
})
.then(funtion(result){ // function on success
return result.data // this is important as AngularJS $http will bind the data from the server into this variable
},
function(errorResponse){
if(errorResponse.status === 401){
// your code for handling 401 errors
}
else if(errorResponse.status === 404){
// your code for handling 404 errors
}
else if(errorResponse.status === 500){
// your code for handling 500 errors
}
})
.then(function(data){
// another function to process data
return data
})
.then(function(data){
// yet another function to process data
return data
})
We can combine as many then callback, as we want.
you can read more about promised from this link: https://scotch.io/tutorials/javascript-promises-for-dummies
Hope it will help you.

Not getting status error code in angular js promise

I would like to know the status code for the error in this function. I currently get this: GET http://localhost:3000/latest-live?lu=1448920013000 net::ERR_EMPTY_RESPONSE
Anyone know if I am handling my error function wrong? I know why I am getting the error, but to better handle the error I would like to know what the status code is (EX: status code 404 not found).
app.factory('getSpreadsheetData', ['$q', '$http', function($q, $http) {
return function getData() {
var deferred = $q.defer();
var req = $http({
url: 'http://localhost:3000/latest-live',
method: 'GET',
cache: false
});
req.success(function(object) {
var data = object.data;
deferred.resolve(data);
});
req.error(function(err) {
deferred.reject(err);
});
return deferred.promise;
}
}]);
Usually that error is thrown, if there is definitly no reponse from the server you are trying to reach in your URL.
Make sure that localhost is actually known to your system and something is running at port 3000 that can return something to your client.
Make sure that the route is known on your server /latest-live and that it didn't crash before returning any information.
First thing I would do is to check, if the URL is actually returning something, when you try to call it in your browser.
Update: Status Codes
Usually the status code is in the response, so you might want to check, if there is anything in err or response, depending on what function is called:
response.status – Number – HTTP status code
response.statusText – String – HTTP status text
Best regards

Understanding angular's promises

I'm learning Angular but having troubles to understand a point. I want to get data from my server. I used this code (from angular's doc) :
this.myFunction = new function()
{
var uri = this.getServerUri() + "Connexion/"+login+"/"+CryptoJS.MD5(pass);
var promises = $http.get(uri)
.success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
//alert("success:"+JSON.stringify(data));
return data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return "";
});
return promises;
};
But I don't understand the behavior of the success function. When I call this function, it works fine, I get the server's answer but I get the full promises object (I have to write "promises.data" to get my data). Could you explain me why ? Because in the success function I tried to return only the data.
EDIT : I forgot to add my calling function :
var serverAccepted = this.MyFunction().then(function(promise) {
var objet = promise.data;
if(!_.isEmpty(objet))
{
userService.setUser(objet, true);
return "";
}
else return "Error while trying to login on the server";
});
return serverAccepted;
Thank you
A promise is a way of dealing with asynchronous requests without blocking. Instead of waiting for a slow operation (like waiting for the server to respond), before executing any more code, a promise is returned and code execution continues.
As such, when you call myFunction in your code above, it will return a promise object.
The promise then provides some neat methods for registering a callback to run code when the server does respond. These methods are .then() and catch() for the success and error case respectively.
Passing a function as an argument will call that function when the server has responded, and you can do with the data what you like. Take this example:
console.log("About to send request");
myFunction()
.then( function (myData){
console.log("Response received", myData);
})
.catch( function (myError){
console.error("Oh dear", myError);
});
console.log("Request sent!");
This will log the following to the console:
About to send request
Request sent!
Response received, {}
EDIT
In response to your comment:
why does the response received contains the full promise object and not only the data?
Because I return the data in the success function.
The response you receive does not contain a promise at all, but your function returns a promise. In your example serverAccepted is assigned the return value of myFunction() which is a promise and will not change.
The line:
.success(function(data, status, headers, config) {
return data;
})
Doesn't really do anything at all - returning a value from a .then() or .success() function is only useful if you are going to chain several calls to .then() together - it provides a way of manipulating values without an ever increasing pyramid of nested callbacks. Returning a value from within a promise callback (function passed to .then() or .success()) will not change the promise itself (serverAccepted in your case).
For example:
assume http GET some/url on your server returns:
{
foo: 'bar',
dorks: 12
}
When we use $http.get This data is inside the response object as a field called data. Most of the time we don't care much about the rest of the stuff, if the call was successful, we just want the data, so we can return it from the first .then() call (normally inside your service) so that any subsequent .then() calls (e.g. inside a controller) can access the data object directly.
myPromise = $http.get('some/url')
.then( function (response) {
console.log(response); //--> { status: 200, data: { foo:....}}
return response.data;
})
.then( function (data) {
console.log(data); //--> {foo: 'bar', dorks: 12}
return dorks;
})
.then( function (data) {
console.log(data); //--> 12
})
console.log(myPromise); //--> promise object with .then() .catch() etc
// ^ THIS WILL NOT CHANGE.
When I call this function, it works fine, I get the server's answer but I get the full promises object
Sounds like you are doing something like this:
var data = service.myFunction();
Above would be fine in synchronous world, however in case of asynchronous operations this is not correct usage of the promises. Instead you should use returned promise then/success methods:
service.myFunction().then(function(data) {
console.log(data);
});
$http request ,returns a promise with two $http specific methods: success and error. success function waits until request is to be finish with success (that means after $http promise resolves), and error function will execute only after the $http promise is rejected.

Angular $http.get with dynamic route?

I'm fairly new to angular and I'm trying to understand how to query from a REST API using a scope variable to determine the URI that is being pulled in the get request.
Lets say I'm in my app.controller and it has a service that spits out an array of numbers.. and for the sake of making the code minimal, I'll skip to the important part:
$scope.currentCompanyId = '0001';
$http.get('/api/'+ $scope.currentCompanyId +'/c').
success(function(data, status, headers, config) {
$scope.cData = data;
}).
error(function(data, status, headers, config) {
// log error
});
I know this is cheating because the $http.get is in the controller. I know it needs to be a factory of some kind.. but I have no idea how to pass the $scope.currentCompanyID to the get request and have it return the JSON. Furthermore, if $scope.currentCompanyID were to change to another number, say... '0002'.. how would the $scope.cData change to reflect the new query?
I don't think using $http in your controller is cheating - one reason for putting it into a factory/service is make it reusable. If you are only doing it in one place a service doesn't add much.
That being said, your service can return a function that takes a parameter:
app.factory("service", function($http) {
return {
getCompany: function(companyId) { ...make $http call and return data... }
}
});
then in your controller:
service.getCompany($scope.currentComanyId).then(function(resp) {...})
You should consider using Angular $resource because it handles a lot of your abstractions. Either way, if you want to make a new request based on changes in the scope variable, you can $watch it:
$scope.$watch('currentCompanyId', function() {
if(!$scope.currentCompanyId) return;
$http.get().success(); // replace with whatever mechanism you use to request data
});
Your request wont launch if currentCompanyId change... You need to lauch your request manually .
otherwise, it seem to be correct
Did you look at $resource service? http://docs.angularjs.org/api/ngResource/service/$resource - it is rather convenient way to REST requests, and docs have quite a few examples that should suit you well
About changing $scope.currentCompanyID - it seems that you need to create watch for this case:
scope.$watch('currentCompanyID', function(newValue, oldValue) {
// do your update here, assigning $scope.cData with the value returned
// using your code:
$http.get('/api/'+ $scope.currentCompanyId +'/c').
success(function(data, status, headers, config) {
$scope.cData = data;
}).
error(function(data, status, headers, config) {
// log error
});
});
You simply need to pass the data in when calling your service. In your controller, you would need to include your service as a DI module and address it as so:
window.angular.module('myControllerModule', [])
.controller('myController', ['$scope', 'myHTTPService',
function($scope, myHTTPService){
$scope.currentCompanyId = 1;
$scope.lookupPromise = myHTTPService.get($scope.currentCompanyId);
$scope.lookupPromise.then(function(data){
//things to do when the call is successful
},function(data){
//things to do when the call fails
});
}]);
In your service, you deal with that value like this:
window.angualr.module('myHTTPServiceModule', [])
.factory('myHTTPService', '$http',
function($http){
function callHTTP(url){
return $http.get('/api/' + url + '/c');
}
return {
get: callHTTP
};
});

Angular issue with Q library

I am following the tutorial related to Angular.js that is located on pluralsight.com. So far I did not have serious issues. Tutorials are very good and easy to follow. However, there is something that I cant resolve on my own. I would like my deffereds to react differently either on success or on fail. In this case they are always firing like they succeeded.
Service:
var resource = $resource('/data/event/:id', {id: '#id'});
return {
getEvent: function (id) {
var deferred = $q.defer();
resource.get({id: id},
function (event) {
console.log("This is (EVENT): " + event);
deferred.resolve(event);
},
function (response) {
console.log("This is (RESPONSE): " + event);
deferred.reject(response);
});
return deferred.promise;
Controller:
$scope.event = eventData.getEvent(2)
.then (
function(event) {
$scope.event = event;
console.log(event);
},
function(response) {
console.log(response);
}
);
In other words, if I send the wrong id (to load JSON file that does not exists) I want it to let me know that.
The $resource service in angular wraps a call to the $http service applying some REST conventions to the HTTP request. These are all documented here in the $resource docs.
When handling the promise from $resource, if the HTTP status code is 200, then the success callback will be executed. Otherwise (if the HTTP status code is in the 400 or 500 range), the error callback will be executed. This is a typical REST convention.
So, since your service is always returning 200 Status codes, $resource assumes that this is a successful server call and executes the success callback.
In order to handle this, you would need to use $http directly or change your service so that it returns the correct HTTP status code.
Hope this helps.

Categories

Resources