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]
});
Related
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.
I have an angular app that displays an editable grid to the user. The user is allowed to edit multiple rows in the grid, then save all those changes at once. When they do this, I am using $q.all to send out all the update calls to an API in parallel.
I want to be able to display an error to the user for each call that fails with some of the information about the object that wasn't saved correctly, but I can't figure out how to get that information out of the error handler.
var ops = []
_.each($scope.items, function (item) {
if(item.Modified)
ops.push(dataService.update(item.itemID, item.otherField))
})
$q.all(ops)
.then(function (repsonse) {
//success
},
function (response) {
//in here I want to output the itemID and otherField values for the item(s) that failed
})
Each item that is being sent out on the API call has a few properties on it (itemID and otherField). I want those values to be include in the error message to the user.
Is this possible using q.all or do I have to use another method?
$q.all will trigger the error callback if any of the operations fail at any time, but in your case you just want to record which ones (if any) failed without triggering any additional error. I would use return a promise from .catch:
ops.push(dataService.update(item.itemID, item.otherField))
.catch(function () {
return {
status: "failed",
id: item.itemID
};
});
Then in $q.all's callback you can iterate over the responses and check for .status == "failed" and check those IDs.
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.
I am trying to figure out how handle success and error on binded methods that return an http response.
I am trying to bind a factory method to a controller, so I am able to call it from the view. This method will try to add an item to the shopping cart via an http request. If it fails the method that is being called will return false, however if it succeeds it will return the http response.
I want to be able to somehow add success or error callbacks to the binded method. Is this possible?
Car controller
// Controller to display cars page
app.controller('carsController', function($scope, $rootScope, inventoryFactory) {
// Function that will fetch JSON and save all necessary data for us to use
function init() {
// Bind these method calls to our cart factory
// Allow method to be called from the view
$scope.addToCart = userFactory.addToCart;
// Get list of items in car category
inventoryFactory.getItems('cars').success( function(data) {
$scope.items = data;
});
}
init();
});
userFactory
// Add item of given ID to shopping cart
factory.addToCart = function(itemID) {
// Validate our user / token
data = factory.getUserToken();
if (data === false) {
return false;
}
req = {
method: 'PUT',
url: 'routes.php/api/shoppingcart/' + itemID,
headers: {
'X-Api-Token': data.apiToken,
'UserID': data.userID
}
};
return $http(req);
};
What you are doing will work, but I would recommend a few changes.
First, direct binding may cause problems. If your factory method at any point needs to call this, it will lose it, since this becomes the $scope rather than the returned object from the factory.
$scope.addToCart = userFactory.addToCart; // addToCart will have "this" be of $scope
Whereas you can keep it either by proxying or by wrapping:
$scope.addToCart = function(id) {
return userFactory.addToCart(id);
};
guarantees that addToCart inside the factory has the correct context for this.
Second, while you can do what you want, if you are returning a promise (as $http() does), then sometimes returning false and sometimes returning a promise can lead to messy code and difficulty testing. You may be better off always returning a promise and rejecting it as necessary:
var defer, ret;
data = factory.getUserToken();
if (data === false) {
defer = $q.defer();
defer.reject();
ret = defer.promise;
} else {
req = {
method: 'PUT',
url: 'routes.php/api/shoppingcart/' + itemID,
headers: {
'X-Api-Token': data.apiToken,
'UserID': data.userID
}
};
ret = $http(req);
}
return ret;
This way you will always have a promise, and can always do
addToCart(25).then(function(){/* success */),function(){/* failure */});
If you need to do error handling, you might want to handle it inside your controller. So if your template is:
<button ng-click="addToCart(item.id)">Click me!</button>
Then your controller addToClick might be:
$scope.addToCart = function(id) {
userFactory.addToCart(id).then(function(results){
// indicate success on the screen by changing some scope var, e.g.
$scope.message = "Successfully added to cart";
},function(err){
// indicate error on the screen by changing some scope var, e.g.
$scope.message = "Problem adding to cart: "+err;
});
};
I have an AngularJS $resource:
App.factory("pjApi", ["$resource", function($resource) {
return $resource("/api/:user/:action/:post_id/", {action:"posts"});
}]);
and in my controller, I basically use it like this:
$scope.deletePost = function(post_id) {
$scope.posts.forEach(function(post) {
if (post_id === post.id)
post.$delete({user:"tjb1982",action:"delete",post_id:post.id});
});
}
The server gives a response with status 200, application/json, and body: "1"
What Angular does with this response is to remove the deleted instance of the Resource object, but then Angular replaces it with the response from the server (i.e. "1"), as if I were creating or updating:
posts
[Resource { 0="1", $$hashKey="00D", $get=function(), more...}]
etc.
So my template is updated with this new (mostly blank) information, which is what I'm trying to avoid. I've tried returning nothing from the server or "0"-- returning nothing results in the Resource instance being preserved entirely and returning "0" results in the same as returning "1".
What response is Angular looking for in order for this Resource instance to be removed entirely so that my template renders correctly?
Calling $delete on a resource only sends the HTTP request to the server; if you want to remove the item from some client side representation--such as an array--you must do so yourself.
So, for your example, you might try something like the following:
$scope.deletePost = function(post_id) {
$scope.posts.forEach(function(post, index) {
if (post_id === post.id) {
post.$delete({user:"tjb1982",action:"delete",post_id:post.id}, function() {
$scope.posts.splice(index, 1);
});
}
});
}
// instance is cleared on success
post.$delete({/* request data */}, function() {
// remove empty element from array
$scope.posts = $scope.posts.filter(function(el) {
return el.id !== undefined;
});
});