$resource $delete won't remove Resource object - angularjs

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;
});
});

Related

not able to push the data in to the list

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]
});

angular $resource receive extra information

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.

Checking if an Angular resource response is empty

I have a service that returns a promise. I would like to check if the value returned is an empty object. How can I achieve this. I guess I need to extract the returned value from the promise object somehow.
Here's my resource:
app.factory('Auth', ['$resource',
function($resource) {
return $resource('/user/identify', {}, {
identify: {
url: '/user/identify'
},
});
}
]);
Then in a service:
Auth.identify(function(data) {
// This always passes since data contains promise propeties
if (!_.isEmpty(data)) {
}
});
A console log of data gives:
Resource
$promise: Object
$resolved: true
__proto__: Resource
I can check for expected properties when the object is not empty but would like a more generic method.
To unwrap the return value you can access the promise directly:
Auth.identify()
.$promise
.then(function(data) {
if (!_.isEmpty(data)) {
// ...
}
});
You're so close. You shouldn't call the response "data" when it is in fact a Resource object, which looks a lot like the Resource object you posted:
$resource('/api/...').get({...}).then(function(response) {
if (response.data) {
// the response has a data field!
}
}
// note: this is using query(), which expects an array
$resource('/api/...').query({...}).then(function(response) {
if (response.length > 0) {
// the response data is embedded in the response array
}
});

Restangular: getList with object containing embedded array

In my AngularJS project I'm trying to use the Restangular getList method but it's returning an error because the API response is not directly an array but an object containing an array.
{
"body": [
// array elements here
],
"paging": null,
"error": null
}
The Restangular error message is:
Error: Response for getList SHOULD be an array and not an object or something else
Is it possible to tell Restangular that the array it's looking for is inside the body property?
Yes, see the Restangular documentation. You can configure Restangular like so:
rc.setResponseExtractor(function(response, operation) {
if (operation === 'getList') {
var newResponse = response.body;
newResponse.paging = response.paging;
newResponse.error = response.error;
return newResponse;
}
return response;
});
Edit: It seems Restangular's API is now changed, for the better, and that the current method to use is addResponseInterceptor. Some adjustments might be needed to the function passed.
I think you should use a the customGET from the Custom Methods
Restangular.all("url").customGET(""); // GET /url and handle the response as an Object
as Collin Allen suggested you can use addResponseInterceptor like this:
app.config(function(RestangularProvider) {
// add a response intereceptor
RestangularProvider.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
var extractedData;
// .. to look for getList operations
if (operation === "getList") {
// .. and handle the data and meta data
extractedData = data.body;
extractedData.error = data.error;
extractedData.paging = data.paging;
} else {
extractedData = data.data;
}
return extractedData;
});
});

$resource Custom Action Replaces my Scoped Variable with the Server Response

I am using a $resource with a custom action.
myApp.factory('User', [ '$resource', function($resource)
{
return $resource('/QuantumServer/users/:id.json',
{
id : '#id'
},
{
resetPassword :
{
method : 'POST',
url : '/QuantumServer/users/:id/resetPassword.json'
}
});
} ]);
I can retrieve my User objects no problem. The problem is that when I invoke the custom action, the value of my locally-scoped User object gets replaced with the server response. This is a problem because the server response is { success : true }, which causes my local object to loose all its field values.
$scope.resetPassword = function()
{
$scope.userBeingEdited.$resetPassword(
{}, function(value, responseHeaders)
{
alert('Password reset');
// The value of $scope.userBeingEdited has been replaced with the
// server response - how to stop this from happening?
});
};
I know that the RESTful philosophy states that e.g. a POST to a resouce would update that resource (on the server) and then return a copy of the updated resource. I undertand that this is how AngularJS $resouce.$save works. But must it really apply to my custom actions?
This is one workaround that I'm aware of, which causes a copy of the object to be updated which we then discard. Is this the most graceful way?
$scope.resetPassword = function()
{
angular.copy($scope.userBeingEdited).$resetPassword(function(value, responseHeaders)
{
alert('Password reset');
});
};

Resources