Dynamic $resource URL based on params - javascript

I'm trying to hit an endpoint that looks like /shop/<item_id>
However, because it also requires an auth token, I am sending an array to my $resource along with the token.
APIshop.create([CreateCampaignService, {auth_token: User.token}]
How can I make it so APIshop is able to take something like this:
APIshop.create([CreateCampaignService, {auth_token: User.token}, {item_id: Cart.item_id}]) and hit /shop/<item_id>?
Here is APIshop:
app.provider('APIshop', function(API_URL) {
this.$get = ['$resource', function($resource) {
var shop = $resource(API_URL.url + API_URL.loc + ':service/', {service: '#service'}, {
'create': {method:'POST', isArray: false, params: {service: 'shop'}},
'update': {method:'PUT', isArray: false, params: {service: 'shop'}}
});
return shop;
}];
});

You can't send it in the array since it is only accepting one param unless you concatenate the token and item id. That is bad practice. You should include the token in the header of the request.
take a look here for more info
Basic Authentication with a Guid token for REST api instead of username/password

In https://docs.angularjs.org/api/ngResource/service/$resource under paramDefaults
Default values for url parameters. These can be overridden in actions
methods. If any of the parameter value is a function, it will be
executed every time when a param value needs to be obtained for a
request (unless the param was overridden).
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.
If the parameter value is prefixed with # then the value of that
parameter will be taken from the corresponding key on the data object
(useful for non-GET operations).
So this should work with TOKEN stored somewhere ($rootScope maybe):
app.provider('APIshop', function(API_URL) {
this.$get = ['$resource', function($resource) {
var shop = $resource(API_URL.url + API_URL.loc + ':service/:id', {service: '#service', id:'#id'}, {
'create': {method:'POST', isArray: false, params: {service: 'shop', access_token: TOKEN}},
'update': {method:'PUT', isArray: false, params: {service: 'shop', access_token: TOKEN}}
});
return shop;
}];
});

Related

Angular Resource: Error: $resource:badcfg

I have looked at other posts here but still can't resolve my issue.
I get a Error:
$resource:badcfg Response does not match configured parameter
I believe this error is caused by it returning an array rather than an object or vice versa.
Here's my code:
Inside my factory(I added the isArray: false but still not luck)
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET', isArray: false},
});
Then in the return section of my factory
find: function(id){
return task.get({ id: id });
}
In my Flask server it does the correct response when I load the page:
127.0.0.1 - - [28/Feb/2017 14:35:13] "GET /task/1 HTTP/1.1" 200 -
However I still get the error?
Also this is what my server provides if i put it in the browser
http://localhost:5000/task/1
[{"completed": true, "id": 1, "ownerName": "Ryan", "task": "Test Activity", "title": "A test task"}]
Iv also tried this and get the same error:
console.log(task.get({ id: 1 }));
This error occurs when the $resource service expects a response that can be deserialized as an array but receives an object, or vice versa. By default, all resource actions expect objects, except query which expects arrays.
To resolve this error, make sure your $resource configuration matches the actual format of the data returned from the server.
--AngularJS Error Reference - $resource:badcfg
The default actions for $resource are:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
Avoid changing the default method:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
//AVOID changing default
//'get': {method:'GET', isArray: true},
});
Instead use the query action method:
find: function(id){
//return task.get({ id: id });
//INSTEAD use query action method
return task.query({ id: id });
}
This will avoid confusing programmers that are familiar with the conventions of the $resource service.
For more information, see AngularJS $resource API Reference.
Using transformResponse to Extract Object from Array
If the server is erroneously returning the resource object enclosed in an array, it can be extracted with a response transform function:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET',
transformResponse: function(json) {
var data = angular.fromJson(json);
if ((typeof data == 'array' && (data.length == 1)) {
return data[0];
} else {
return data;
};
}
}
});
Have you tried with something like:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET', isArray: false},
});
You've missed {id:'#id'} to pass paramter id
Turns out I needed to set isArray: false to true
so:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET', isArray: true},
});

How to create CRUD using Django/Tastypie API?

I'm setting up a project using django-tastypie REST API and AngularJS. I'm fine with reading things from the json file through angular, but I cannot find a decent tutorial that would show me how to make even a simple CRUD application that isn't saving all the information in an object or whatever, but is manipulating the database through the tastypie api. Can any of you show me a tutorial of such sort or maybe just show me some sample code for this?
Thank you.
Use $resource - A factory which creates a resource object that lets you interact with RESTful server-side data sources.
Let's say you have Django model Book, and tastypie resource named BookResource. It's URL is /api/v1/book/. As you know, this URL actually is a resource, that means you can manipulate data in your Book model with GET, POST, DELETE, etc. requests.
You can "map" the Angular $resource to this API resource in a way:
someModule.factory('bookResource', ['$resource', function($resource) {
var apiResourceUrl = "/api/v1/book/:bookId/";
// id - your model instance's id or pk, that is represented in API resource objects.
var resource = $resource(apiResourceUrl, {bookId: '#id'}, {
all: {
method: 'GET', params: {}, // GET params that will included in request.
isArray: true, // Returned object for this action is an array (miltiple instances).
},
get: {
method: 'GET',
},
// [Define custom save method to use PUT instead of POST.][2]
save: {
/* But, the PUT request requires the all fields in object.
Missing fields may cause errors, or be filled in by default values.
It's like a Django form save.
*/
method: 'PUT',
},
// [Tastypie use POST for create new instances][3]
create: {
method: 'POST',
},
delete: {
method: 'DELETE',
},
// Some custom increment action. (/api/v1/books/1/?updateViews)
updateViews: {
method: 'GET',
params: {"updateViews": true},
isArray: false,
},
});
}]);
someModule.controller('bookCtrl', ['$scope', '$routeParams', 'bookResource',
function ($scope, $routeParams, bookResource) {
if ("bookId" in $routeParams) {
// Here is single instance (API's detail request)
var currentBook = bookResource.get({bookId: $routeParams.bookId}, function () {
// When request finished and `currentBook` has data.
// Update scope ($apply is important)
$scope.$apply(function(){
$scope.currentBook = currentBook;
});
// And you can change it in REST way.
currentBook.title = "New title";
currentBook.$save(); // Send PUT request to API that updates the instance
currentBook.$updateViews();
});
}
// Show all books collection on page.
var allBooks = bookResource.all(function () {
$scope.$apply(function(){
$scope.allBooks = allBooks;
});
});
// Create new
var newBook = new bookResource({
title: "AngularJS-Learning",
price: 0,
});
newBook.$save();
}]);
Angular's docs provide more information how to make usage of resource really incredibly.
Here is the problem with urls. As I remember, Angular will send request to /api/v1/books/1 (without slash in the end) and you'll get 404 from tastypie. Let me check this.
[2] http://django-tastypie.readthedocs.org/en/latest/interacting.html#updating-an-existing-resource-put
[3] http://django-tastypie.readthedocs.org/en/latest/interacting.html#creating-a-new-resource-post

AngularJS, creating a resource hash

Trying to take a url and in an AngularJS controller redirect that request to the best place to get the json data. The VideoSearchCtrl is bound to the search form. The url generated is correct for the template so I'm using the controller to redirect it to the place for the json data.
GuideControllers.controller('VideoSearchCtrl', ['$scope', 'VideoSearch',
function($scope, VideoSearch) {
var pattern = new RegExp(".*/search\\?=(.*)");
var params = pattern.exec( document.URL )[1];//redirect to videos to execute the search for the data
$scope.videos = VideoSearch.query({ resource: "videos", action: "search", q: params });
}
]);
This sends /videos/search?q=xyz in to the query. The factory creates the resource:
var VideoSearchServices = angular.module('VideoSearchServices', ['ngResource']);
VideoSearchServices.factory('VideoSearch', ['$resource',
function($resource){
return $resource("/:resource/:action:params", {resource: "#resource", action: "#action", params: "#params"}, {
query: {
isArray: true,
method: "GET",
headers: {
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
}
});
}
]);
But the server gets the url as /videos/search%fq=xyz, not /videos/search?q=xyz and therefore the "show" method is being invoked instead of a custom "search" action. Obviously there is some escaping somewhere? Or maybe the "?" is also a special pattern the resource factory looks for? Probably obvious to someone used to AngularJS or javascript for that matter.
I have a template for search and the json is retrieved from a different location. Both work but I can't ask for the json with the above code.
First, do:
return $resource("/:resource/:action", {resource: "#resource", action: "#action"}, {
Then:
$scope.videos = VideoSearch.query({ resource: "videos", action: "search", q: params });
The point is params are not a part of the url you have to declare to the resource, you just declare resource and action then you add params which is natural for all routes
Actually there is a better way to do this with $location from Angular and jQuery's extend that should work with any future params that are added. Will only have to add the new params to the query factory.
GuideControllers.controller('VideoSearchCtrl', ['$scope', '$location', 'VideoSearch',
function($scope, $location, VideoSearch) {
var route = jQuery.extend(
{ resource: "videos", action: 'search' },
$location.search()
);
$scope.videos = VideoSearch.query(route);
}
]);

AngularJS Caching a REST request

I'm new to angularJS and have a question about caching etc.
I have a wizard with two steps, I want to be able to click back and next and have the forms still filled out as the user had them.
In my page1Partial i have this:
<li ng-repeat="pick in picks | orderBy:orderProperty">
<b><span ng-bind="pick.name"/></b>
<input type="checkbox" ng-model="pick.checked" ng-click="updateBasket(pick)">
</li>
When i go to the next page, then click back the checkboxs are cleared, and its because my RESful call to a java service is called again. How can I cache this response?
From my controller, this hits my REST web service every time.
$scope.picks = Pick.query();
My service
angular.module('picksService', ['ngResource']).
factory('Pick', function ($resource) {
return $resource('rest/picks/:id', {}, {
'save': {method: 'PUT'}
});
});
Since 1.1.2 (commit), all the $httpConfig options are directly exposed in $resource action objects:
return {
Things: $resource('url/to/:thing', {}, {
list : {
method : 'GET',
cache : true
}
})
};
if you replace $resource with $http then you can directly use below code
$http({
method: 'PUT',
url: 'url',
cache:true
});

Extend AngularJS $resource to map to Rails RESTful Routes (particularly $save to create AND update)

I'd like to map the default AngularJS $resource actions:
'get': {method:'GET'}
'save': {method:'POST'}
'query': {method:'GET', isArray:true}
'remove': {method:'DELETE'}
'delete': {method:'DELETE'}
to these 5 Rails RESTful routes:
'show': {method:'GET'} -> get
'create': {method:'POST'} -> save
'update': {method:'PUT'} -> save
'index': {method:'GET', isArray:true} -> query
'delete': {method:'DELETE'} -> remove or delete
Everything maps well except for save. It works on create as a POST request. But I'd like to change it to a PUT request when I'm saving a resource that has an id != null
# This factory needs a smarter $save() method
myServices.factory 'Message', ['$resource', ($resource) ->
Message = $resource "api/users/:user_id/messages/:id",
user_id: '#user_id'
id: '#id'
,
# update should not be needed
update:
method: 'PUT'
Message
]
myApp.controller "UsersShowCtrl", ["$scope", "$routeParams", "User", "Message", ($scope, $routeParams, User, Message) ->
$scope.user = User.get({id: $routeParams.user_id})
$scope.myMessage = new Message {user_id: $routeParams.user_id}
$scope.submitMyMessage = ->
# This should just be:
# $scope.myMessage.$save()
# rather than:
unless $scope.myMessage.id?
$scope.myMessage.$save()
else
$scope.myMessage.$update()
]
I would ultimately like to modify the the submitted params as well so that I can namespace them to the default rails nested params i.e. params[:message] rather than attaching them directly to the root params string, but first I need to know how best to modify the the $resource.
Off the top of my head, how about this save() method?
myServices.factory 'Message', ['$resource', ($resource) ->
Message = $resource "api/users/:user_id/messages/:id",
user_id: '#user_id'
id: '#id'
,
# update should not be needed
update:
method: 'PUT'
Message::save = ->
if #id?
#update()
else
#create()
Message
]

Categories