I have an ngResource model representing a REST API endpoint. I'd like to set some default fields on this model, so that the data that's POSTed is always correct. Ideally I'd like somethings like this:
resource = new Resource({field1: 'value1');
resource.$save();
// POST with data: { field1: 'value1', field2: 'defaultvalue' }
This answer seems to cover the same case, but I'd rather have the defaults in place immediately after constructing the Resource, rather than filling in at the last minute before POSTing. (The field I'd like to default is an array, in case that makes a difference).
My factory looks like this:
.factory('Resource', ['$resource',
function($resource) {
var Resource = $resource('/api/resource/:resourceId', {}, {
query: {method: 'GET', isArray: true}
}}
return Resource;
}
])
I've tried poking around in the prototype of the Resource before returning it, but can't figure out how to insert a default value there.
Related
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
I'm using ngResource to handle my models in my Ionic/Angular app and I'm having trouble figuring out how/if I can make custom actions on the resource.
I'm storing my model instances in local storage and when I update a record, I want to update the local storage as well. I have this working, but I'm having to copy and paste code for multiple instances and would like to keep it DRY.
LogEntry.update($scope.timeLog, function(data) {
// update local storage
for ( var i = 0; i < logEntries.length; i++) {
if(logEntries[i].id == $scope.timeLog.id){
logEntries[i] = $scope.timeLog;
}
};
localStorageService.set('LogEntries', logEntries);
});
Here is a situation where I update a record, and after the promise returns I update local storage. I would like to make this repeatable, how I envision it being possible (based on other things I've seen in other frameworks and other languages) is something like:
LogEntry.update($scope.timeLog, function(data) {
// update local storage
LogEntry.updateLocalStorage($scope.timeLog);
});
My resource looks like:
.factory('LogEntry', function(config, $resource) {
return $resource(config.apiUrl + 'logentries/:id/', {}, {
'update': {
method:'PUT',
params: { id: '#id' }
}
});
})
Maybe I'm missing something in the docs, but it's pretty short and I'm not seeing a way to do this. Is something like LogEntry.updateLocalStorage($scope.timeLog); possible to store with ngResource, or do custom actions like that need to come from somewhere else? I'd like to keep my model-related actions together if possible.
You could use the transformResponse method in your resource definition. It's kind of a hack since you don't actually need to alter the response, but it allows you to preform actions with the returned data:
{function(data, headersGetter)|Array.<function(data, headersGetter)>}
transform function or an array of such functions. The transform function takes the http response body and headers and returns its transformed (typically deserialized) version. By default, transformResponse will contain one function that checks if the response looks like a JSON string and deserializes it using angular.fromJson. To prevent this behavior, set transformResponse to an empty array: transformResponse: []
https://docs.angularjs.org/api/ngResource/service/$resource
.factory('LogEntry', function (config, $resource) {
return $resource(config.apiUrl + 'logentries/:id/', {}, {
'update': {
'method': 'PUT',
'params': {
'id': '#id'
},
'transformResponse': function (data, header) {
// do stuff with data
return data;
}
}
});
})
That way it will always execute when you get a response but you could also use the transformRequest method, which will always fire on request regardless if you're getting a response.
{function(data, headersGetter)|Array.<function(data, headersGetter)>}
transform function or an array of such functions. The transform function takes the http request body and headers and returns its transformed (typically serialized) version. By default, transformRequest will contain one function that checks if the request data is an object and serializes to using angular.toJson. To prevent this behavior, set transformRequest to an empty array: transformRequest: []
https://docs.angularjs.org/api/ngResource/service/$resource
Which you choose depends on your usecase. Using transformRequest you could always save to localstorage, even when remote is down.
I have a more generic implementation of $resource as "Datarepository" factory in my application and all the REST API calls calls this factory to do the "REST" operation
myapp.factory('DataRepository', function ($resource) {
var resourceFactory = function (url) {
return $resource(url, {}, {
update: { method: 'PUT' }
}
);
}
return {
invokeAPI: resourceFactory
}
});
A sample call to the repository get method looks like this
DataRepository.invokeAPI(myappURL).get();
For a specific scenario alone, i would like to "cache" the data.. I dont want to disturb the "Datarepository" factory method and just would like to add the cache paramter for those URL's which i would like to cache the data
something like this
DataRepository.invokeAPI(myappURL).get({cache:true});
The above implementation doesnt work the way it is expected and it passed the cache as query string paramter. I read the angularjs documentation for $resource. I got it how to set it at $resource level but i m not sure how to pass it to the resource through normal function call without disturbing the Factory implementation
I am learning angularjs and i want to ouput some json data on console.
I am doing something like this
$scope.events = events.query();
but when i print on the console
console.log($scope.events);
it gives me the output like
Array []
how can i print the data like this
[
{"id":18,"file":{"url":"/uploads/playlist/file/18/01_-_MashAllah.mp3"},"event_id":23,"created_at":"2015-11-11T10:33:52.000Z","updated_at":"2015-11-11T10:33:52.000Z","name":"01 - MashAllah.mp3"},
{"id":19,"file":{"url":"/uploads/playlist/file/19/02_-_Laapata.mp3"},"event_id":19,"created_at":"2015-11-11T10:50:01.000Z","updated_at":"2015-11-11T10:50:01.000Z","name":"02 - Laapata.mp3"}
]
below is my whole code
.controller('ShowEventsCtrl', ['$scope','events', function($scope,events) {
$scope.events = events.query();
console.log($scope.events);
}]);
services
angular.module('myApp')
.factory('events', ['$resource',function($resource) {
return $resource('/events', {},{
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
})
}])
and when i print the data in html like this
<div ng-repeat="playlist in playlists">
{{playlist}}
</div>
I get the correct output so i but how to output it in console.
Because your events.query() method is asynchronous, you need to log stuff after the action is resolved. Easiest way I can think of is
$scope.events = events.query(function(events) {
console.log(events);
});
From $resource
The action methods on the class object or instance object can be invoked with the following parameters:
HTTP GET "class" actions: Resource.action([parameters], [success], [error])
...
Success callback is called with (value, responseHeaders) arguments
I'm guessing your confusion comes from this feature of $resource
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.
You see the correct data in your template because once the data is returned from the server, a scope digest is triggered and your template is updated.
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.