Here's my resource:
.factory('Posting', ['$resource', function ($resource) {
return $resource('api/Postings/:action/:arg', {}, {
findByParent: { method: 'GET', params: { action: 'parent', arg: '#guid' }, isArray: true },
findByReference: { method: 'GET', params: { action: 'reference', arg: '#reference' }, isArray: true }
});
}]);
In my controller I'm using my resource as this:
Posting.findByParent({ guid: parent_guid },
function (success) {
...
},
function (error) {
...
});
This returns the URL /parent?guid=0ff646e9-4397-4654-b8d2-118c6258023a
However, using my resource like this:
Posting.findByParent({ arg: parent_guid },
function (success) {
...
},
function (error) {
...
});
Gives me the correct URL: /parent/0ff646e9-4397-4654-b8d2-118c6258023a
I thought the point with using an '#' was to give parameters better names?
I'm also wondering if I still should use $resource even tho my API isn't really RESTful.
Is it better to give my custom (unRESTful) functions their own URL? Something like:
findByParent: { method: 'GET', url: 'api/Postings/parent/:guid', params { guid: '#guid' }, isArray:true }
By default, if you define a parameter on the path (like you did with arg), and you pass in an object that has a matching key, like in the second example, that key will be used to resolve the path.
If however, there are no matching parameter, the keys of the object passed in will resolve to query parameters, like in the first example.
To set custom default resolves, you need to specify them in the second argument to resource, like this:
.factory('Posting', ['$resource', function ($resource) {
return $resource('api/Postings/:action/:arg',
{
action: '#action',
arg: '#guid'
},
{
findByParent: { method: 'GET', params: { action: 'parent' }, isArray: true },
findByReference: { method: 'GET', params: { action: 'reference' }, isArray: true }
});
}]);
This should make action resolve to what is specified in findByParent and findByReference, and arg to whatever value is passed in for key guid.
You could experiment with setting an # in the respective methods 'guid' property, but for your usecase, it does not seem to be necessary.
to answer your second question: you can specify several parameter controllers on a single path element (level). The only condition is that you don't use / specify resolutions for more than one of them in a single method. That is, you could do api/Postings/:action:anotherController/:arg, as long as you would specify resolutions for :action and :anotherController in separate methods.
Please find this awesome post by Ben Nadel http://www.bennadel.com/blog/2433-using-restful-controllers-in-an-angularjs-resource.htm with an example use
Related
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.
I'm using Angular's $resource to interface with an API, and creating custom methods on that resource. One of these methods is a POST, and when I attempt to use it, it's sending the entire resource, not just the properties I'm attempting to post to the API. I don't think this is the intended behavior of the $resource service, but then, I might be missing something.
Here's the code:
The service:
angular.module('adminApp')
.factory('Framework', function($resource) {
return $resource('/api/frameworks/:id', {id: '#id'}, {
'update': {
method: 'PUT'
},
'getRequiredLicenses': {
method: 'GET',
url: '/api/frameworks/:id/required_licenses',
isArray: true
},
'addRequiredLicenses': {
method: 'POST',
url: '/api/frameworks/:id/required_licenses'
},
'removeRequiredLicense': {
method: 'DELETE',
url: '/api/frameworks/:id/required_licenses/:license_id'
}
});
});
Where I'm calling it:
scope.addLicensesToFramework = function() {
scope.framework.$addRequiredLicenses(null, {
required_licenses: Object.keys(scope.selectedLicenses) // returns an array of ints
});
}
(Note that this is in a directive. scope.framework is the instance of the framework resource)
When this request is sent, here's what's being included in the payload:
My intention is to only pass {'required_licenses': [12345,1236]} in the payload, and I can't seem to figure out why it's sending the entire resource as the body. (It's, in fact, not sending this at all, only the original resource)
Any insight would be really helpful, thanks!
Try calling it like this:
scope.addLicensesToFramework = function() {
scope.framework.$addRequiredLicenses({
required_licenses: Object.keys(scope.selectedLicenses),
id: 1234
}, function(resp){ console.log(resp) });
}
Also notice that I included the id in the parameters object.. you'll probably need that.
I'm receiving the following error from $resource:
Error: [$resource:badcfg] Error in resource configuration for action `query`. Expected response to contain an array but got an object
the API is not returning an Array but it return this:
{
list: [...items...],
next: true,
limit: 100,
last: 0
}
I need to get the entire object with query() and push list in my $scope.items.
The other params needs for pagination or infinite scroll.
How can I do that?
EDIT:
This is my factory:
angular.module('app').factory('Items', ['$resource',
function ($resource) {
return $resource('/items/:id', { id: '#id' }, {
'query': {
method: 'GET',
isArray: false
},
update: {
method: 'PUT'
}
});
}
]);
Your factory is right, just remove single quotes from query:
...
query: {
method: 'GET',
isArray: false
}
...
EDIT after below comment
I had exactly the same problem few days ago, which was solved by setting isArray to true. So I quite confident in syntax, but for me I have slightly different declaration of factory:
angular.module('docAccessApp',[]).factory('Access',function($resource){
return $resource('http://localhost:8080/documentAccesses/:id',{id:'#_id'},{
query: {
method: 'GET',
isArray:false
}
});
});
Maybe you need to Hard Reload the page so that browser refreshes all the js cache.
I have a custom resource defined as such:
app.factory('SpaceGroupService', ['$resource', function ($resource) {
return $resource('api/SpaceGroup/:id', { id: '#id'},
{
'parkingSpaces': { method: 'GET', url: 'api/SpaceGroup/:id/ParkingSpaces', isArray: true }
});
}]);
The idea is to pass in an ID of my space group object to return all parking spaces in that group. When I call
SpaceGroupService.query()
While running on my local iisexpress it's hitting the correct service url:
/api/SpaceGroup
But when I do:
spaceGroup.$parkingSpaces({ id: spaceGroup.SpaceGroupId }, function (parkingSpaces) {
spaceGroup.parkingSpaces = parkingSpaces;
});
It is querying my restful service as:
/api/SpaceGroup/2
instead of:
/api/SpaceGroup/2/ParkingSpaces
I'm somewhat new to angularJS and I know my route works on my API controller so I'm just trying to get this service to work properly. Any ideas why this overwritten URL isn't called?
I'd would write your service like this instead:
app.factory('SpaceGroupService', ['$resource', function ($resource) {
return
{
ParkingSpaces: $resource('api/SpaceGroup/:id/ParkingSpaces', {},
{
query: { method: 'GET', params: { id: '#id'}, isArray: true }
}
}
}]);
And then call it like this instead:
SpaceGroupService.ParkingSpaces.query({ id: someId });
You can set up multiple resources in the service this way - and you can define multiple methods too (GET, POST, PUT, DELETE). It also means that Angular will honor your $resource url.
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.