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

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

Related

How to contact a non-standard API using Angular ngResource

The API I am using requires a non-standard where clause if I try to search for a particular non-id field. The endpoint I need is:
http://127.0.0.1:4001/api/testusers/findOne?userName=Anton
So this will find me the first record in the testusers table whose column (userName) = 'Anton'.
My standard service is:
angular.
module('shared.testUser').
factory('TestUser', ['$resource',
function($resource) {
return $resource('http://127.0.0.1:4001/api/testusers/:id', {id:'#id'},//parameters
{
update: {
method: 'PUT' // To send the HTTP Put request when calling this custom update method.
}
});
}
]);
and my calling function is:
self.checkUsersEntryDirection = function(){ //NOT WORKING
self.testuser = TestUser.get({ username: 'anton' }, function() {
console.log(angular.toJson(self.testuser));
}); // get() returns a single entry
}
Clearly this doesn't work and I can't use the standard get approach. Can anyone think how this can be achieved?
You could create a secondary factory TestUserByName, and make the following changes:
angular.
module('shared.testUser').
factory('TestUserByName', ['$resource',
function($resource) {
return $resource('http://127.0.0.1:4001/api/testusers/findOne?userName:username', null,
{
update: {
method: 'PUT' // To send the HTTP Put request when calling this custom update method.
}
});
}
]);
Call the get action method with two parameters:
var params = {id: "findOne", username: "anton"};
self.checkUsersEntryDirection = function(){
self.testuser = TestUser.get(params, function() {
console.log(angular.toJson(self.testuser));
}); // get() returns a single entry
}
The id parameter will override the default and username parameter will be added as a query string.
From the DOCS:
Each key value in the parameter object is first bound to url template if present and then any excess keys are appended to the url search query after the ?.
Given a template /path/:verb and parameter {verb:'greet', salutation:'Hello'} results in URL /path/greet?salutation=Hello.
--AngularJS ngResource $resource Service API Reference

Saving subresource with Angular's $resource

I have a factory defined which returns a $resource:
myApp.factory('Region', function($resource) {
return $resource(baseUrl + '/templates/:templateId/regions/:regionId', null, {
query: {
method: 'GET',
isArray: false
},
update: {
method: 'PUT'
}
});
});
As you can see, a region is a subresource of a template, and I've defined the endpoint as /templates/:templateId/regions/:regionId.
My issue comes when I want to save a new region. How do I specify the templateId to save the region to? Here's my snippet:
$scope.save = function() {
if ($scope.mode === 'edit') {
// TODO
} else {
Region.save($scope.region, function(success) {
$state.go('app.templateList')
});
}
};
In every other resource I have I've just used Model.save($scope.model);, I don't know how to specify other URL parameters and the Angular docs don't seem to cover it.
According the docs, non-GET (e.g. PUT) methods accepts following arguments
Resource.save([parameters], postData, [success], [error]).
Where parameters is a path params and it is optional, postData – body of the request. If you want to provide templateId, just add it as first argument:
Region.save({templateId: 'id'}, $scope.region, function(success) {
$state.go('app.templateList')
});
I've faced similar dillema. I thought about some generic convention where to create subresource X eg as a new element of a collection owned by some resource Y I would do
POST /api/Y/<yId>/X
then to access collection of X owned by Y:
GET /api/Y/<yId>/X
However for modifying or deleting subresource we could access subresource directly:
PUT /api/X/<xId>
DELETE /api/X/<xId>
to achieve above we could use $resource definition as
Subresource = $resource('/api/:parent/:parentId/subresource/:id',
{ id: '#id' },
{
'update': { method:'PUT' } // this is because Angular lacks PUT support
});
then we can use it like
var subresourceList;
Subresource.query({parent: 'Y', parentId: parentId },
function(result) {
// handle result here
subresourceList = result;
});
and after modifying single subresource object we can save it using
var subresource = subresourceList[0];
subresource.someProp = 'newValue';
subresource.$update()
with earlier subresource definition the $update will do PUT directly to /api/X/<xId> which is reasonable whenever subresource X object in terms of being modified has nothing to do with its owning Y.

actions in $resource in angularjs

I have a webapi back end with the following method in the ProductController :
[HttpGet]
[Route("api/product/FindName")]
public Product FindName(string name){
return name & "Hello "
}
I am trying to make use of $resource in the frontend.
var resource = $resource('api/product/:id', {});
resource.query() will return all items which are exposed in the server side using the GetALL() method. This works fine .
What exactly is the {action} in the $resource does ? I have seen examples for the POST, but what if is set
var resource = $resource('api/product/:id', {}, { FindName: { method: 'GET', params: { name: 'phone' } } });
will this call the method FindName in the backend ? or what exactly it does, I mean the parameter if I set the 'GET' in method.
I am calling as
resource.FindName({ name: 'phone' }, function () {
});
But the backend is not getting fired . i see the call that is being requested to the server from fiddler is
Demo/api/product?name=phone
The resource declaration is incorrect. It should be
var resource = $resource('api/product/:id', {}, { FindName: { method: 'GET', params: { id: 'phone' } } });
This defaults the id placeholder to value phone.
For invocation now you can do
resource.FindName({}, function () { //gets api/product/phone
});
or override id part
resource.FindName({id:'tablet'}, function () { //gets api/product/tablet
});
Resource has a built in GET function that should be able to be used without the need to define the extra FindName action that has been added to $resource.
If you changed the route on your webapi to be
[HttpGet]
[Route("api/product/{name}")]
public Product FindName(string name){
return name & "Hello "
}
Then you could use resource like this to get data back from this route.
var resource = $resource('api/product/:id', {}, {});
resource.get({ id: 'phone' }, function () {
});
If you wanted the name params to match on both you could change :id to :name and in the resource.get change id to name also.
I hope this helps.

Passing 'filter' parameter to angular resource (DreamFactory rest api)

I am having an issue with query parameters from my AngularJS app
I am reading documents from MongoDB using DreamFactory rest api like this:
.service('Servant', ['$resource', function($resource) {
// define and return $resource
return $resource('https://mydsp.cloud.dreamfactory.com:443/rest/mongodb/tablename',
{
// set params to bind too
app_name: 'myapp',
fields: '#fields',
limit: '#limit',
offset: '#offset',
filter: '#filter'
},
{
// set update method to 'PUT'
update: {
method: 'PUT'
}
}
)
}]);
This all works great when I set filter like "parameter=value" but I failed to find a way of passing more complicated filter param in JSON format as described here, using $in parameter etc. Does anyone know the right syntax for this?
EDIT:
just tried something like
filter = angular.toJson("{'parameter':{$in:['value1','value2']}}")
with no success...
First...drop the port from your service url. 'https' for dreamfactory specifies port 443. No need for you to do it explicitly. Second...You should be able to pass a SQL style filter as a string in your params. When you set up your $resource the way you have you should be able to pass a params object to it. No need to stringify or toJson anything. DreamFactory should handle it. For example...
Here is your service:
.service('Servant', ['$resource', function($resource) {
return $resource('https://mydsp.cloud.dreamfactory.com/rest/mongodb/tablename',
{
app_name: 'myapp',
fields: '#fields',
limit: '#limit',
offset: '#offset',
filter: '#filter'
},
{
update: {
method: 'PUT'
}
}
}]);
Calling that service with a params object:
// the 'parameter' value in our filter string should relate to a field and/or property
scope.paramsObj = {
fields: '*',
limit: 10,
offset: 0,
filter: 'parameter in (5,15)'
}
// call service and handle promise returned by $resource
Servant.get(scope.paramsObj).then(
function(result) {
// handle success
// like assign to a var or something
// here we just log it
console.log(result)
},
function(error) {
// handle error
// probably should throw an error here
// but we just log it here
console.log(error);
});
EDIT
Ok. So...it should work with SQL style filter strings. An issue has been logged with DreamFactory. In the mean time you can create a custom $resource action to handle the filters and tunnel your GET request through a POST. Easier then it sounds. See code below.
Here is the service with custom action
.service('Servant', ['DSP_URL', '$resource', function (DSP_URL, $resource) {
return $resource(DSP_URL + '/rest/mongohq/Colors', {
// params to bind to
app_name: YOUR_APP_NAME_HERE,
fields: '#fields',
limit: '#limit',
offset: '#offset'
}, {
// custom $resource action
'getFiltered': {
// set our method to post because we have to post
// our filter object
method: 'POST',
// We can transform the data before the post.
// In the circumstance we do need to stringify
// So that's what we do here.
transformRequest: function (data) {
return JSON.stringify(data);
}
}
})
}]);
Here is the controller:
.controller('MongoCtrl', ['$scope', 'Servant', function ($scope, Servant) {
// Create a params object
// This requests all fields.
// And we explicitly set the method to
// GET. We are tunneling a GET request
// through our POST because our filter
// needs to be posted but we really want a GET.
$scope.params = {
fields: '*',
method: 'GET'
};
// Call our Service with our custom $resource action
Servant.getFiltered(
// Send our params
$scope.params,
// Send our filter as post data
{
"filter": {
"color": {
"$in": ["blue", "white"]
}
}
},
// handle success
function (data) {
console.log(data)
},
// handle error
function (error) {
console.log(error)
})
}])
I guess you should stringify your filter data:
resource.update( {
filter: JSON.stringify( {qty:{$in:[5,15]}} )
});
Or in this way:
resource.get({id:123}, function() {
resource.filter = JSON.stringify( {qty:{$in:[5,15]}} );
resource.$update();
});

$resource $delete won't remove Resource object

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

Resources