Add a custom method to $resource in Angular JS - angularjs

I have written a custom method to do a call to my API passing some extra parameters, but I get the following error:
TypeError test.testing is not a function.
I followed the code in here: https://docs.angularjs.org/api/ngResource/service/$resource
This is the code I use, $save for example is working fine.
I am using version 1.4.1.
services.factory('Calendar',function($resource){
return $resource('/api/calendar/:id',{
update: {
method: 'PUT'
},
testing: {
method: "POST", params:{charge:true}
}
});
});
function( $scope, Calendar ) {
var test = new Calendar();
test.title = "hello";
test.$testing();
...
}

The reason is because you haven't quite followed the example in the docs. See:
angular.module('testApp', [])
.factory('CreditCard', creditCard)
.factory('Calendar', calendar);
function creditCard() {
return $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'#id'}, {
charge: {method:'POST', params:{charge:true}}
});
}
function calendar() {
return $resource('/api/calendar/:id',
{id: 200},
{
update: {
method: 'PUT'
},
testing: {
method: "POST", params:{charge:true}
}
});
}
As you can see I added that second object argument {id:200} that tells the ng-resource that in order to fill out /api/calendar/:id correctly it needs to pull the property id from that object and use its value.

Related

$resource PUT operation not extracting id

I can't seem to get an "id" to come through to the $resource function from the controller. Here is the offending code...
Controller:
$scope.update_user_extra = function () {
UserExtraResource.update($scope.user_extra_details, function (data) {
$scope.user_extra_details = data;
$scope.user_extra_details = {mobile:data.mobile,
landline:data.landline,
position:data.position,
notes:data.notes,
language:data.language};
});
};
Resource:
module.exports = function ($resource) {
return $resource('/api/user_extra/:id/', { id: '#_id' }, {
details: {method: 'GET', url: '/api/user_extra/details'},
update: {method: 'PUT'}
});
};
The GET works fine but the custom PUT returns:
http://127.0.0.1:8000/api/user_extra/ 404 (Not Found)
hardcoding the id like:
return $resource('/api/user_extra/1/', { id: '#_id' }, {
works fine. Any help is much appreciated!!
hmm ... changing this line to:
return $resource('/api/user_extra/:id/', { id: ̶'̶#̶_̶i̶d̶'̶ '#id' }, {
seems to have done it. Thank you very much!
If the default parameter value is prefixed with #, then the value for that parameter will be extracted from the corresponding property on the data object. For example, if the defaultParam object is {someParam: '#someProp'} then the value of someParam will be data.someProp.
In the above question, the PUT operation was trying to extract the property _id of the data object when the name of the property was actually id.
For more information, see AngularJS $resource API Reference.

Unable to use $update, $remove and $save in ngresource

I am using ngresource as follows but unfortunately I am unable to access the $update, $remove and $save methods in this way. What am I doing wrong?
angular.module('myApp.services').factory('Entry', function($resource) {
return {
method1: $resource('/api/entries/:id', { id: '#_id' }, {
update: {
method: 'PUT' // this method issues a PUT request
}
}),
method2: $resource('/api/entries2', {}, {
})
});
// not working: Entries is not a function at Scope.$scope.save
var entry = new Entries({});
entry.method1.$update();
entry.method1.$save();
entry.method1.$delete();
On the other hand, this works:
angular.module('myApp.services').factory('Entry', function($resource) {
return $resource('/api/entries/:id', { id: '#_id' }, {
update: {
method: 'PUT' // this method issues a PUT request
}
});
});
var entry = new Entries({});
entry.$update();
entry.$save();
entry.$delete();
So your second example doing $resource('http://example.com/resource.json') is the correct usage of that construction, while the first one is not.
After executing var entry = new Entries({}); you get entry as factory instance in your controller, which has available actions that you've defined for it.
UPD
You can have multiple resources in the service - https://stackoverflow.com/a/17163459/405623. In your example you've just missed the ['ngResource'] DI in your module.

Invalidate $resource Cache After Post Request

I am using $resource and caching the results of get requests. My problem is that, after post requests, the cache is not being invalidated.
Here is the return value from the service:
return $resource('http://url.com/api/url/:id', {}, {
'query' : {
method : 'GET',
isArray:true,
cache : true
},
'get' : {
method : 'GET',
cache : false
}
})
Here is the save method I am using inside my controller. As you can see, I'm using the callback on the post request to recalculate the query/list of nouns.
var newNoun = new Noun($scope.noun);
newNoun.$save(function(x) {
$scope.nouns = Noun.query();
});
I would like to invalidate the cache after calling post or another non-get method. How could I do this? Is this already built into $resource or do I need to implement it on my own?
You could create a wrapper service to do the caching like you want, for example:
app.factory('cachedResource', function ($resource, $cacheFactory) {
var cache = $cacheFactory('resourceCache');
var interceptor = {
response: function (response) {
cache.remove(response.config.url);
console.log('cache removed', response.config.url);
return response;
}
};
return function (url, paramDefaults, actions, options) {
actions = angular.extend({}, actions, {
'get': { method: 'GET', cache: cache },
'query': { method: 'GET', cache: cache, isArray: true },
'save': { method: 'POST', interceptor: interceptor },
'remove': { method: 'DELETE', interceptor: interceptor },
'delete': { method: 'DELETE', interceptor: interceptor },
});
return $resource(url, paramDefaults, actions, options);
};
});
Then replace any $resource with cachedResource.
Example plunker: http://plnkr.co/edit/lIQw4uogcoMpcuHTWy2U?p=preview
While #runTarm's answer above is great, it does not allow actions to be easily customized from the inheriting service, e.g. the following would not be possible:
app.factory('Steps', function (CachedResource) {
return CachedResource('/steps/:stepId', {}, {
save: { method: 'POST', params: { stepId: '#stepId' } }
});
});
In this case, this definition of save would be replaced by the one present in CachedResource.
Solution
But it can be fixed easily from Angular 1.4 by replacing
actions = angular.extend({}, actions, {
with
actions = angular.merge({}, actions, {
so that both objects are deep-merged.
Even better solution
In the above scenario, action options defined in CachedResource would be preferred over custom configuration in inheriting services. To fix that, switch the order of arguments passed to merge:
actions = angular.merge({}, { /* default options get, query, etc. */ }, actions);
With this solution, the following will work as expected (i.e. use DESTROY instead of default DELETE when calling remove):
app.factory('Steps', function (CachedResource) {
return CachedResource('/steps/:stepId', {}, {
remove: { method: 'DESTROY' }
});
});
$resource is using the default cache for $http.
You can access it using: $cacheFactory.get('$http')
You can remove a key value pair, using the returned caches remove({string} key) method.
E.g.:
var key = '...the key you want to remove, e.g. `/nouns/5`...';
$cacheFactory.get('$http').remove(key);

Updating JSON file with AngularJS

I would like to update a JSON file using my AngularJS app.
Here is my service:
myGallery.factory('galleryData', function ($resource,$q) {
return $resource('./data/gallery.json', {}, {
update: { method: 'PUT' },
'query': { method: 'GET', isArray: true }
});
});
My controller is:
myGallery.controller('GalleryController',
function GalleryController($scope, galleryData)
{
$scope.galleries = galleryData.query();
$scope.addGallery = function (newGallery) {
$scope.galleries.push({
name: newGallery.name
});
newGallery.name = "";
};
$scope.saveGallery = function () {
$scope.saveGallery.$update();
// ???
};
});
but in the save method, I don't know what I have to do.
Any idea?
You cannot update a file just like that. You will need to write some server-side handling for that situation and update the file manually with request data.
Implementation depends on what server technology do you use.

Can someone give me an example on how I can call $resource directly?

In my code I have:
var EntityResource = $resource('/api/:entityType', {}, {
postEntity: { url: '/api/:entityType/', method: 'POST' },
getEntity: { url: '/api/:entityType/:entityId', method: 'GET' },
putEntity: { url: '/api/:entityType/:entityId', method: 'PUT' },
deleteEntity: { url: '/api/:entityType/:entityId', method: "DELETE" },
getEntities: { url: '/api/:entityType/:action/:id', method: 'GET', isArray: true },
});
Then I am using the following to get data:
getProjects: function (
entityType,
deptId) {
var deferred = $q.defer();
EntityResource.getEntities({
action: "GetProjects",
entityType: entityType,
deptId: deptId
},
function (resp) {
deferred.resolve(resp);
}
);
return deferred.promise;
},
and the following to call getProjects:
entityService.getProjects(
'Project',
$scope.option.selectedDept)
.then(function (result) {
$scope.grid.data = result;
}, function (result) {
$scope.grid.data = null;
});
I think the intermediate function getProjects is not needed and I would like to directly use $resource.
Can someone give me some advice on how I could do this? I looked at the AngularJS documentation for $resource and it's not very clear for me.
$resource calls by default return empty arrays and then fill them up when the response is received. As mentioned in documentation
It is important to realize that invoking a $resource object method
immediately returns an empty reference (object or array depending on
isArray). Once the data is returned from the server the existing
reference is populated with the actual data.
There are default 5 methods already defined on resource, get,save,query,remove,delete. You can directly call these rather than defining your own as you have done like postEntity, but the url template remains the same.
So once you define resource like this
var entityResource = $resource('/api/:entityType');
you can make calls like
var entity=entityResource.get({entityType:1},function(data) {
//The entity would be filled now
});
See the User example in documentation
If you want to return promise then you have to wrap the calls into your your service calls like you did for getProjects.
Update: Based on your comment, the definition could be
var entityResource = $resource('/api/:entityType/:action/:id')
Now if you do
entityResource.get({},function(){}) // The query is to /api
entityResource.get({entityType:'et'},function(){}) // The query is to /api/et
entityResource.get({entityType:'et',:action:'a'},function(){}) // The query is to /api/et/a
entityResource.get({entityType:'et',:action:'a',id:1},function(){}) // The query is to /api/et/a/1
Hope it helps.
$resource does expose $promise but it is on return values and subsequent calls.

Resources