How do you catch errors/read http status code when making a rest call in one of these formats, both work to return a successful response, just no idea how to grab the info i need. I can get the object with values returned as I need, I just cant get the http status code.
methods provided by #Claies in a response to this question (Get data from $resource response in angular factory)
$scope.makeRestCall= function () {
$scope.member = Item.makeRestCallWithHeaders('123456789', '789456123')
.query().$promise.then(function(response){
});
};
$scope.makeRestCall= function () {
$scope.member = Item.makeRestCallWithHeaders('123456789', '789456123')
.query({}, function() {
})
};
I have tried to use the first method here and grab something from the function(response) such as response.status, but it returns undefined.
For reference, using this factory:
.factory("Item", function($resource) {
var endpoint = "http://some valid url";
function makeRestCallWithHeaders(id1, id2) {
return $resource(endpoint, null, {
query: {
method: 'GET',
headers: {
'id1': id1,
'id2': id2
}
}
})
}
var item = {
makeRestCallWithHeaders: makeRestCallWithHeaders
}
return item ;
})
Item returns something like this:
{firstName:Joe, lastName:smith}
I am really just trying to figure out how I can access the status code returned by the REST call. Absolute end goal is to read any error response and return error to UI written in angular as well. If there is a way to just read this in the UI, that could work too.
To read the error status you need to pass in the errorCallback to the $promise:
$scope.makeRestCall= function () {
$scope.member = Item.makeRestCallWithHeaders('123456789', '789456123')
.query().$promise.then(
function(response){
//this is the successCallback
//response.status & response.statusText do not exist here by default
//because you don't really need them - the call succeeded
//see rest of answer below if you really need to do this
// but be sure you really do...
},
function(repsonse) {
//this is the errorCallback
//response.status === code
//response.statusText === status text!
//so to get the status code you could do this:
var statusCode = response.status;
}
);
};
You shouldn't need the status in the successCallback, because it is a success and you know the success code implicitly.
Therefore the status is not available in the successCallback by default.
If, for some reason, you do need the status in your successCallback, you could write an interceptor to put this information somewhere, but be aware that the angular framework deals with the data differently in different success scenarios so you will need to write code for different cases.
Related
I have this code
PedidosService.getProductbyID($scope.listProductos.ProductID).then(function (d) {
$scope.oneProduct = d.data.producto;
});
PedidosService is the name of my factory, getProductbyId is my http get request and $scope.oneProduct is the variable where i want store the result of this request.
factory.getProductbyID = function (id) {
return $http.get('/Pedidos/GetProduct/' + id);
}
factory is my Factory and getProductbyID is my function to call http request
I call this code in a button. The first time that I click in the button, it returns a empty response '[]' but the next times that I click the button, it works fine!!!.
Thanks for you help
As far as I know the $http.get returns a promise because that call is asynchronous. With that in mind a typical call to $http.get should be something like this:
$http.get('/someUrl', config).then(successCallback, errorCallback);
In your context I would have done it this way, so the factory returns a promise and in your controllers you will handle the success and error callback appropriately:
factory.getProductbyID = function (id) {
var deffered = $q.defer();
$http.get('/Pedidos/GetProduct/'+ id)
.then(function (result) {
deffered.resolve(result);
}, function (data) {
deffered.reject(data);
});
return deffered.promise;
}
I have one list which contains list of objects so, now i want to add one more object to the each list in the main list
I am using AngularJS
here is the code which i tried
$scope.mediaList = [];
$scope.getTheProfile = function(data){
for(var i in data)
{
ProfileService.getByHandle(data[i].handle,function(profiledata)
{
$scope.mediaList[i].displayName = profiledata.name.displayName
},
function(data , status){
console.log("In Error");
if(status == '400'){
$scope.errors.push(data["ERROR"])
}
},
function(data , status){
console.log("In forbidden")
})
}
alert($scope.mediaList[0].displayName)
}
so i am trying to add displayName to that list
now the problem is i am not able to get the value in that alert
if that alert is inside ProfileService.getByHandle function then i am getting the value
this is the function getByHandle
this.getByHandle = function(handle, successCB, errorCB, forbiddenCB) {
console.log("In Profile Service for fetching the profile by handle: "+handle);
HttpCommunicationUtil.doGet(apiConstants["profile"]["getByHandle"]+"/"+handle, successCB, errorCB, forbiddenCB);
};
Looking at getByHandle, it seems you are making asynchronous HTTP request using HttpCommunicationUtil.doGet.
What happens is this: for loop will make the HTTP calls and trigger this alert alert($scope.mediaList[0].displayName) without waiting for the response of getByHandle, as it's an asynchronous request.
Therefore, when you try to alert there will be an empty array [] value for $scope.mediaList due to line#1. So $scope.mediaList[0].displayName will produce error saying unable to get displayName of undefined.
You can return promises in ProfileService.getByHandleand when it's resolved use .then to update your variable.
If you can post code for HttpCommunicationUtil.doGet it'll be more useful.
EDIT:
Without HttpCommunicationUtil.doGet, I'll give you an idea of how to do it in a generic way.
Service:
this.getByHandle : function(params) {
return $http.get('/api/endpoint');
}
Controller:
ProfileService.getByHandle(params).then(function(data){
//use data to push response in $scope.mediaList[i]
});
As you guys know, Angular recently deprecated the http.get.success,error functions. So this kind of calls are not recommended in your controller anymore:
$http.get("/myurl").success(function(data){
myctrl.myobj = data;
}));
Rather, this kind of calls are to be used:
$http.get("/myurl").then(
function(data) {
myctrl.myobj = data;
},
function(error) {
...
}
Problem is, simple Spring REST models aren't working with this new code. I recently downloaded a sample code with the above old success function and a REST model like this:
#RequestMapping("/resource")
public Map<String,Object> home() {
Map<String,Object> model = new HashMap<String,Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
This should return a map like {id:<someid>, content:"Hello World"} for the $http.get() call, but it receives nothing - the view is blank.
How can I resolve this issue?
The first (of four) argument passed to success() is the data (i.e. body) of the response.
But the first (and unique) argument passed to then() is not the data. It's the full HTTP response, containing the data, the headers, the status, the config.
So what you actually need is
$http.get("/myurl").then(
function(response) {
myctrl.myobj = response.data;
},
function(error) {
...
});
The expectation of the result is different. Its the response and not the data object directly.
documentation says :
// Simple GET request example:
$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.
});
Properties of the response are
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.
As the data object is required,
Please convert the code as
$http.get("/resource").then(
function(response) {
myctrl.myobj = response.data;
});
then must be return a new promise so you should handle it with defers.
var myApp = angular.module('myApp', []);
myApp.factory('modelFromFactory', function($q) {
return {
getModel: function(data) {
var deferred = $q.defer();
var items = [];
items.push({"id":"f77e3886-976b-4f38-b84d-ae4d322759d4","content":"Hello World"});
deferred.resolve(items);
return deferred.promise;
}
};
});
function MyCtrl($scope, modelFromFactory) {
modelFromFactory.getModel()
.then(function(data){
$scope.model = data;
})
}
Here is working fiddle -> https://jsfiddle.net/o16kg9p4/7/
How is result passed from the $http object to the unnamed function that is executed on success?
$http
.success(function (result) {
...
})
I know that the result is passed via any variable name that i put into the function. It is typically called result. But how is this done? It seems like wizardry to me.
I would expect to have to write something like:
$http
.success(function (result=$http.result) {
...
})
You have to study how both Javascript Function Paramters and Promises work.
The code that you pasted comes, I Think, from some AngularJS Application.
If my assumption is correct, $http is a service and doesn't have anyone success method.
The success method is present on $http methods:
//get, post, ecc...
$http.get(...).success()
By the way:
Javascript doesn't provide any way to match parameters, their order is always the order provided by the callee and the names that you use is just for you (Don't confuse with the IOC that the DependencyInjection in AngularJS does). EXAMPLE 1
function loggerCase1(log1, log2, log3, log4) {
console.log('loggerCase1 => param-1:', log1);
console.log('loggerCase1 => param-2:', log2);
console.log('loggerCase1 => param-3:', log3);
console.log('loggerCase1 => param-4:', log4);
console.log('---------------------');
};
function loggerCase2(log4, log2, log1, log3) {
console.log('loggerCase2 => param-1:', log4);
console.log('loggerCase2 => param-2:', log2);
console.log('loggerCase2 => param-3:', log1);
console.log('loggerCase2 => param-4:', log3);
console.log('---------------------');
};
function loggerCaseN() {
for(var i = 0; i < arguments.length; i++) {
console.log('loggerCaseN => param-' + (i + 1) + ': ', arguments[i]);
}
console.log('---------------------');
};
var logs = ['log1', 'log2', 'log3', 'log4'];
loggerCase1.apply(this, logs);
loggerCase2.apply(this, logs);
loggerCaseN.apply(this, logs);
If it's all clear about function parameters behaviour in javascript... you will know that isn't possibile to say give me the first as the second or something like that, also, the example that you pasted seems similar to default parameters (implemented in ES6, aka Javascript Harmony).
Let's go to the point 2:
In a simple promise chain (find on google or see the link above) you can pass a result to the next callback using return. EXAMPLE2
angular
.module('promisechainging', [])
.run(function($q) {
$q
.when('Hello World')
.then(function(greetings) {
console.log('ring 1', greetings);
return greetings;
})
.then(function(salut) {
console.log('ring 2', salut);
return salut;
})
.then(function(ciao) {
console.log('ring 3', ciao);
return { message: ciao };
})
.then(function(result) {
console.log('ring 4', result.message);
return result;
})
.catch(function(error) {
console.log('THIS LOG NEVER HAPPENS BECAUSE THERE AREN\'T REJECTED PROMISES');
return $q.reject(error);
})
.finally(function() {
console.log('We Are At The END');
})
;
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="promisechainging"></div>
Basically is not important how parameters are named!
Angular is using the promise mechanism which basically returns an object that let you know when the result is available or an error has been thrown.
When the ajax call returns, angular is calling the promise and providing the result as a parameter.
It's just like calling a regular function.
$http allows you to perform async network operations and returns a promise object (you can read more about promises in Angular here).
The success and error methods were used to declare callbacks to what happens when the promise is resolved (when the request was successfully completed) or rejected (when there was an error at processing the request). I used the past tense since they are now deprecated and the desired way to handle these is using the then method of the promise object.
// Simple GET request example:
$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.
});
Basically, the syntax is pretty much the same - the successCallbackFunction has the same signature as the method you were passing in the success method of your example.
But this is only the method signature. Your callback function parameters can be called however you want (result, data etc). All you have to keep in mind is that the first parameter in your callback function is going to be the data returned by your request.
$http
.success(function (result) {
...
})
$http will return a Promise Object which is nothing but a Javascript Object with success and different other functions.
So the statement immediately becomes like below as $http is evaluated,
(Promise Object)
.success(function (result) {
...
})
The success function of promise will save this anonymous function to be called once the promise is resolved. We can manually resolve promises, but I guess http will do this for you here.
Once http request(AJAX) is successful angular will tell this Promise object to run this success function by resolving the Promise, somewhat like:
suceess: function(responseData){ //success of AJAX
resolve(responseData); //this will pass the result to promise
}
Once resolve is called promise object has the result with it, it will then call the success function you passed initially with this value of result.
PS: This is a rough idea, I ave to look into Angular source to see their actual implementation.
Javascript functions are also class objects.
When $http completes it will call either the success or fail function - they are objects so they can be passed around. When it does, it will provide the parameters.
I am using ng-resource to do ajax request. I want to send extra info besides the data.
For example, I have an article entity on my server
exports.fetchArticle = function(req, res, next) {
var article = req.article
return res.json({data: article, message: 'success fetch article'})
}
The reason I wrap it is that, in the case of deletion, it makes no sense to send data, I can just return res.json({data: null, message: 'deleted successfully'})
on my client side, I have:
$scope.fetchArticle = function() {
Article.get({articleId: $routeParams.articleId}, function(response) {
$scope.article = response.data
$scope.ajaxSuccess = response.message
}, function(err) {
$scope.ajaxError = err.data.message
})
}
$scope.article is not an instance of ng-resource anymore, thus I can't do further request with $scope.article, i.e. this will cause error, since $scope.article is a plain json object:
$scope.article.$update(function(response) {...})
If I simply return res.json(article) from server, it works, but I can't send along the message.
The reason I dont generate the message from client but fetch from server is that, the error message is from server, I want to keep success message consistent with the error message.
Is there any other elegant way to send the message?
Assuming that all your servers responses follow this format:
{
data: {/*...*/},
message: 'some message'
}
You could use $http's transformResponse for that, so that you get an ngResource instance that is your returned object while still processing your message. For that, you need a transform-function:
function processMessage(data, message) {
//Do whatever you want with your message here, like displaying it
}
function transform(response) {
processMessage(response.data,response.message);
var data = response.data;
delete response.data;
delete response.message;
for(var attributeName in data) {
response[attributeName] = data[attributeName];
}
return response;
}
Then you can add this function to $http's default transfroms in the config of your app:
angular.module("yourApp",[/* ... */])
.config(function($httpProvider){
//....all your other config
$httpProvider.defaults.transformResponse.unshift(transform);
});
Now all repsonses from $http get transformed by this function, triggering processMessage and leaving you with a ngResource instance of the returned object.