Alter $resource default methods - angularjs

How can I alter and add to the default $resource return object methods?
The return object defaults in question are (from the docs):
A resource "class" object with methods for the default set of resource actions optionally extended with custom actions. The default set contains these actions:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
As I understand, this does not follow the restful principles so good in regard to when to use POST and PUT. Perhaps it would be better to have the following methods:
{ 'get': {method:'GET'},
'save': {method:'PUT'},
'create': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
Is there a way to change the default object that $resource creates, so I do not need to alter each resource I am creating with $resource?

I found a talk at ng-conf 2014 by Googles DoubleClick team that was a real good answer to this question and beyond. Basically they create a apiProvider that uses and extends $resource.
They also have a github gist that would be too long to include here.

Related

How do I call $resource service from controller in angularJs

This is get service function where I am calling the API
.factory('Report', function($resource, API_URL) {
return $resource(API_URL +
'security/:userId/1498780800000/listOfDeliveries', {
userId : '#userId',
expected : '#expected',
arg1 : '#arg1'
}, {
update: {
method: 'PUT'
}
});
})
In the app.js I have this below controller
.controller('ReportsController', function($scope, $rootScope,
ProfileData, $state, $timeout, Report) {
})
First of all, you need to check how angular factory & service work.
Your factory return a $resource, so read the doc about $resource
A resource "class" object with methods for the default set of resource
actions optionally extended with custom actions. The default set
contains these actions:
{
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
So, you can use theses methods: Report.get(), Report.save(), Report.query(), Report.remove(), Report.delete()
In addition there are custom method you defined: Report.update()
And you can pass userId as params so:
Report.get({userId: 1234}); will call a GET request to: API_URL+'security/1234/1498780800000/listOfDeliveries
(expected and args1 are not in url so I dont think you need them)
What's returning Report.get() ?
Class actions return empty instance (with additional properties below). Instance actions return promise of the action
So Report.get(...) is returning a promise, you will get data by:
Report.get(...).then(function(data) {
$scope.requestData = data;
});

Factory patterns and ngResource

I have a factory that provides access to a resource object as shown below. Initially, I had this implemented inside a controller, but moved it up into a factory to make it more reusable across my whole app.
When I was building it, a line in the angular doc on providers caught my eye:
Best Practice: name the factory functions as Factory (e.g., apiTokenFactory). While this naming convention is not required, it helps when navigating the codebase or looking at stack traces in the debugger. (source)
I'm obviously not doing that below (as I'm following a pattern from the resource Doc). But then I was trying to figure out what kind of provider this would qualify as and I'm not sure, and they don't include Best practice notes for any of the other kinds of providers detailed. Should I use service() instead of factory()? (I can't really find a clear answer.)
This question obviously has very low stakes, as the application works just fine (better than it did when I wasn't using the factory). But I'm curious about opinions.
...
.factory("Entries",Entries)
...
function Entries($resource,config) {
endpoint = config.endpoint + ":" + config.port + "/entry";
return $resource(endpoint + '/:id', {id:'#id'},
{ 'get': {method:'GET'},
'create': {method:'POST'},
'update': {method:'PUT'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
}
);
}
If it works as a factory, keep it as a factory. I never use the service provider as I find that it doesn't provider anything over the factory method, and it is easier for my brain to grasp the factory pattern.
With a factory, you provide a function that returns the object you want angular's injector to use. With a service, you provide a Constructor function. The injector then creates the object that will be injected by newing up an object using the function provided. Angular factory's and service's are always singletons though so providing a Constructor function to new up a single object seems to me like a bit of overkill.
In your situation, if you converted it to a service, your object would basically just be acting as a proxy, something like below
...
.service("Entries",Entries)
...
function Entries($resource,config) {
var endpoint = config.endpoint + ":" + config.port + "/entry";
var resource = $resource(endpoint + '/:id', {id:'#id'},
{ 'get': {method:'GET'},
'create': {method:'POST'},
'update': {method:'PUT'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
}
);
this.get = resource.get;
this.create = resource.create;
this.update = resource.update;
this.query = resource.query;
this.remove = resource.remove;
this.delete = resource.delete;
}
That's pretty ugly and serves no purpose unless you really want to abstract away that you are using a $resource, which again you could do with a factory just as easily
...
.factory("Entries",Entries)
...
function Entries($resource,config) {
var endpoint = config.endpoint + ":" + config.port + "/entry";
var resource = $resource(endpoint + '/:id', {id:'#id'},
{ 'get': {method:'GET'},
'create': {method:'POST'},
'update': {method:'PUT'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
}
);
return {
get: resource.get,
create: resource.create,
update: resource.update,
query: resource.query,
remove: resource.remove,
delete: resource.delete
}
}
Even the angular documentation can't seem to provide a compelling reason for using a service. The example they give for when a service is most suitable is this (taken from here)
Given that you have an existing Constructor function
function UnicornLauncher(apiToken) {
this.launchedCount = 0;
this.launch = function() {
// Make a request to the remote API and include the apiToken
...
this.launchedCount++;
}
}
a factory provider would look like
myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
return new UnicornLauncher(apiToken);
}]);
while with a service you could make it a oneliner
myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Not too exciting to me and certainly not a good reason to make me go through the thought exercise of whether this should be a service or a factory. My advice is to forget that service() exists and just use factories

Understanding angularJS $resource isArray property

i'm learning angular's $resource service and in the angular tutorial a custom action is added (query) that has its method set to 'get' and isArray is set to true
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
However, if you look at the docs for $resource the 'query' action already has its method set to 'get' and isArray is already set to true by default. So i thought that i can just leave those properties out.
This works for the method property, but it turns out that if i leave out the isArray property i get this error:
Error: [$resource:badcfg] Error in resource configuration for action
query. Expected response to contain an object but got an array
Why is that?
I think you have misunderstood the documentation.
By default without adding any custom actions, the following are supported:
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
So by default the query action expects an array to be returned which makes sense since a query generally would return an array of items.
So if you use:
phonecatServices.factory('Phone', ['$resource', function($resource){
return $resource('phones/phones.json');
}]);
You can then perform a query like so:
var queryParams = { name: 'test' };
Phone.query(queryParams, {}, function (response) {
$scope.phones = response;
});
Now if you wanted to add a custom action then the default for isArray is false so:
return $resource('phones/:phoneId.json', {}, {
someCustomAction: {method:'GET', params:{phoneId:'phones'} }
});
would need to return an object. If an array was returned then isArray would need to be set to true like so:
return $resource('phones/:phoneId.json', {}, {
someCustomAction: {method:'GET', params:{phoneId:'phones'}, isArray: true }
});

Error: [$resource:badcfg] Error in resource configuration. Expected response to contain an array but got an object?

How fix Error:
[$resource:badcfg] Error in resource configuration. Expected response
to contain an array but got an object?
// Service
angular.module('admin.services', ['ngResource'])
// GET TASK LIST ACTIVITY
.factory('getTaskService', function($resource) {
return $resource('../rest/api.php?method=getTask&q=*',{ 'get': {method:'GET'}});
})
// Controller
$scope.getTask = getTaskService.query(function (response) {
angular.forEach(response, function (item) {
if (item.numFound > 0) {
for(var i = 0; i < item.numFound; i++) {
$scope.getTasks[i] = item.docs[i];
}
}
});
});
Also, if your service is sending an object instead of an array add isArray:false to its declaration.
'query': {method: 'GET', isArray: false }
$resource("../rest/api"}).get();
returns an object.
$resource("../rest/api").query();
returns an array.
You must use :
return $resource('../rest/api.php?method=getTask&q=*').query();
First of all you should configure $resource in different manner: without query params in the URL. Default query parameters may be passed as properties of the second parameter in resource(url, paramDefaults, actions). It is also to be mentioned that you configure get method of resource and using query instead.
Service
angular.module('admin.services', ['ngResource'])
// GET TASK LIST ACTIVITY
.factory('getTaskService', function($resource) {
return $resource(
'../rest/api.php',
{ method: 'getTask', q: '*' }, // Query parameters
{'query': { method: 'GET' }}
);
})
Documentation
http://docs.angularjs.org/api/ngResource.$resource
In order to handle arrays with the $resource service, it's suggested that you use the query method. As you can see below, the query method is built to handle arrays.
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
User $resource("apiUrl").query();
Make sure you are sending the proper parameters too. This happened to me after switching to UI-Router.
To fix it, I changed $routeParams to use $stateParams in my controller. The main issue was that $stateParams was no longer sending a proper parameter to the resource.
For anyone coming here in 2021, another way this request may look is as follows (at least it was in our app)
angular.module('load').factory('StateService', ['$resource', 'store', function($resource, store) {
return $resource('/some/url', {}, {
fetchAllStateCodes: {
method: 'GET',
isArray: true, // Response is an array of objects
headers: {
"Authorization": "Bearer " + store.get("jwt"),
"Content-type": "application/json"
}
}
});
}]);
From all of the good answers, it still wasn't obvious to me as to where to put the isArray flag...

Angularjs: How to use $resource to serve an array of objects

I had a $resource that servers two files, names.js and birthday.js:
// names.js
{
name: John,
name: Mary,
}
// birthday.js
{
John: 28,
Mary: 28
}
It works great in my controller. But I couldn't run ng-repeat on it. So I converted the data to a format that was used in the ng-repeat docs:
// friends.js
friends = [{name:'John', age:25}, {name:'Mary', age:28}];
But now my provider for friends.js returns an array of characters (not very useful) or an object of characters. How can I make it so that my provider returns a useful format of the data?
The query in the service is:
friends: $resource('data/friends.js', {}, {
query: {method: 'GET', params: {}, isArray: true[or false]}
})
In short, how do you use a $resource to provide friends.js?
How do you query your $resource?
The get method is expecting an object while the query method is expecting an array.
This is the default methods available on a $resource :
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
More info available here :
AngularJS resource documentation

Resources