I have a resource, Answer, which has a composite key made of QuestionnaireId and QuestionId. The ngResource code is as follows:
function answerResource($resource) {
return $resource("/api/answers/:questionnaireId/:questionId",
{
questionnaireId: "#questionnaireId",
questionId: "#questionId"
}
);
}
I want to query this resource with the the questionnaire Id and get back all the answers. If I use:
answerResource.query(
{
questionnaireId: questionnaireId
}
);
Then the requested url is:
/api/answers/123
When I want it to be:
/api/answers?questionnaireId=123
Otherwise I have two routes that I need to handle for the query search - one with the Id in the querystring, the other with the Id as part of the url path. (I also have queries with search text where the questionnaire Id might not be present, that would use urls like /api/answers?q=sometext).
Surely any .query parameters should be passed as querystrings, not as part of the route. How do I get the desired behaviour?
The best option I can come up with, is to create a new search method on the resource, which doesn't have the parameters in the url:
// normal resource definition here...
,{
search: {
method: "GET",
url: "/api/answers",
isArray: true
}
}
Calling that with the composite key values will append them as querystring parameters and not as part of the url, e.g. /api/answers?questionnaireId=123
Faced a similar issue, but I'm messing with composite key (item, sequence).
1- html.erb: Borrowed option 2 from https://spin.atomicobject.com/2013/11/22/pass-rails-data-angularjs/
That way I could grab inside the .js script the item from the .erb form I already have (I'm doing some kind of "manage details -- crud" on request with angularjs)
2- in the controller .js file I grab the value from the custom data in the html element using document.getElementById and getAttribute, concatenating that to the request: '/items?personId='+div.getAttribute("data-personId")+'&format=json'
3- in the items_controller index action, by default rails g expects no argument and grabs all items, then I had to ask
if params[:personId].present?
#items = Item.where(personId: params[:personId])
else
grab-all-items
end
This works, but I'm not sure if it's the best approach because I'm a real newbie for angularjs.
Just messing with this issue, cannot figure yet how to remove a record.
$resource crete path instead of querystring because you define placeholders :
return $resource("/api/answers/:questionnaireId/:questionId"..
If you delete them from resource-level and then call $resource.query({param: value}) you can make querystring, also you can specify more actions for single $resource to perform different request overriding action url property.
I created a simple (and very fast) example might help you understand:
working example :https://jsfiddle.net/Nedev_so/b71feyc6/19/
EDIT :
after your comment i understand what you need, so
resource factory :
//only for example purpose
var answersBaseUrl = "https://example.com/api/answers";
var answerTemplateUri = commentsBaseUrl + '/:questionnaireId/:questionId'
var params = {questionnaireId: '#_questionnaireId',questionId: '#_questionId'};
var res = $resource(answerTemplateUri, params,{
one :{
method: "GET",
},
all: {
method: "GET",
url: answersBaseUrl,
isArray: true
}
});
return res;
});
and then in your controller :
//get answers by questionnaire
//GET /answers?questionnaireId=123
$scope.answersByQuestionnaire = answers.all({questionnaireId: 123});
//get answers by question
//GET /answers?questionId=123
$scope.answersByQuestion = answers.all({questionId:123});
//get single answer by questionnaire and question
//GET /answers/123/123
$scope.answer = answers.one({questionnaireId: 123,questionId:123 });
(check network logs and you can see expected behaviour)
Related
I am facing a very weird case in my angularjs app. In a factory, the following code works properly:
$http.put(apiBase + 'delete?id='+orderId);
Which obviously connects to an api to perform a PUT operation (it is called "delete" here but it actually only updates a flag in the record).
But the same code, when written this way, does not work:
$http.put(apiBase + 'delete', {
params: {
id: orderId
}
}
);
Which is funny, because I am using the exact same syntax in some other factories to hit similar APIs and they work!
I believe this is because the second argument of $http.put() is a data object, not a config object. You are passing a config object as the second parameter.
You could try:
$http.put(apiBase + 'delete', null, {
params: {
id: orderId
}
}
);
https://docs.angularjs.org/api/ng/service/$http#put
When using $http.put, you don't need to wrap your data in the config object. You can pass the data object directly, and then omit the third parameter:
$http.put(apiBase + 'delete', { id: orderId });
Your other factories probably work with the syntax stated in your question because you are making $http.get or $http.delete requests.
I have found that this slightly-different API for the various "shortcut" methods to be confusing enough that I almost think it's better to avoid them altogether. You can see the differences from the documentation where get and delete have two parameters:
get(url, [config]);
delete(url, [config]);
and most of the other shortcut methods have three:
post(url, data, [config]);
put(url, data, [config]);
Note that the [config] object is defined further up on that documentation page, which is where it defines that "params" property:
params – {Object.} – Map of strings or objects which
will be serialized with the paramSerializer and appended as GET
parameters.
First of all I want to mention that I have been digging around a lot for this. I am unable to find a simple and straight forward answer even in the docs. (Call me dumb if you will, in case it IS mentioned in the docs! I can't seem to find it anyway.)
The thing is, I want to make a PUT request to a URL of the form
app.constant('URL_REL_VENDOR_PRODUCTS', '/api/vendor/:vendorId/products/:productId');
But I do not want to put the vendorId parameter in the request payload. My service layer looks something like this:
services.factory('VendorProductService', function($resource, UserAccountService, URL_BASE, URL_REL_VENDOR_PRODUCTS) {
return $resource(URL_BASE + URL_REL_VENDOR_PRODUCTS, {
vendorId: UserAccountService.getUser().vendorId,
id: '#id'
}, {
update: { method: 'PUT' }
});
});
I know that instead of the vendorId: UserAccountService.getUser().vendorId I could have written something along the lines vendorId: '#vendorId' but then that pollutes my payload doesn't it?
I don't want to keep the mechanism I am already using in the example as the mechanism does not work when you switch accounts i.e.,if the UserAccountService.getUser() is updated. Basically I'm having to reload the entire page to get the service initialized again.
In short, the question is, as the title suggests, how do I set the path parameter vendorId without using a service like the one in the snippet and also without modifying the payload?
Make the parameter value a function:
services.factory('VendorProductService', function($resource, UserAccountService, URL_BASE, URL_REL_VENDOR_PRODUCTS) {
return $resource(URL_BASE + URL_REL_VENDOR_PRODUCTS, {
vendorId: function () {
return UserAccountService.getUser().vendorId
},
id: '#id'
}, {
update: { method: 'PUT' }
});
});
From the Docs:
paramDefaults (optional)
Default values for url parameters. These can be overridden in actions methods. If a 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).
-- AngularJS $resource API Reference
I have a GET endpoint with URI as /user/user-id . The 'user-id' is the path variable here.
How can I set the path variable while making the GET request?
This is what I tried:-
$http.get('/user/:id',{
params: {id:key}
});
Instead of replacing the path variable, the id get appended as query param.
i.e my debugger show the request URL as 'http://localhost:8080/user/:id?id=test'
My expected resolved URL should be like 'http://localhost:8080/user/test'
$http's params object is meant for query strings, so key-value pairs you pass into params are output as query string keys and values.
$http.get('/user', {
params: { id: "test" }
});
Becomes: http://localhost:8080/user?id=test
If you need http://localhost:8080/user/test, you can either:
Construct the url yourself,
$http.get('/user/' + id);
Or, use $resource (specifically $resource.get https://docs.angularjs.org/api/ngResource/service/$resource). This is a little cleaner.
Why not something like this?:
var path = 'test';
$http.get('/user/' + path, {});
From where the data is coming/posted in following code given in angular tutorial.I tried the same using json file but data is not getting posted or updated in json file .
var CreditCard = $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'#id'}, {
charge: {method:'POST', params:{charge:true}}
});
// We can retrieve a collection from the server
var cards = CreditCard.query(function() {
// GET: /user/123/card
The first parameter to $resource() is the url where the data is coming from (for GET) or posted (for POST). Parts of the url not supplied will default to the current page url, so for example if the page using this code is at http://example.com:8080/somepath/example.html then this resource would be getting data from http://example.com:8080/user/123/card/456 (or whatever #id is).
I need one of my backbone models to hit a variety of URLs depending on the type of action being performed. How do I determine the action within the URL function so that I can specify the appropriate URL? For example:
DELETE: /myapipath/itemtype/id/
POST: /myapipath/special-path/
GET: /myapipath/special-path/?code=ABC
I know how to tell the difference between a POST and everything else: this.isNew()
But how do I tell the difference between a DELETE and a GET in a custom model.url function?
Please don't suggest that I change the server-side api. That isn't up to me.
Thanks!
Conceptually the url of a Backbone model is the primary GET url of the resource. To use a different url for some of the actions, override the model's sync function. Fortunately, Backbone makes it easy to override:
window.MyModel = Backbone.Model.extend({
// ... other stuff ...
url: '/myapipath/special-path/?code=ABC',
methodUrl: {
'create': '/myapipath/special-path/',
'delete': '/myapipath/itemtype/id/'
},
sync: function(method, model, options) {
if (model.methodUrl && model.methodUrl[method.toLowerCase()]) {
options = options || {};
options.url = model.methodUrl[method.toLowerCase()];
}
Backbone.sync(method, model, options);
}
}
Edit: I took another look at the Backbone source and noticed that it merges the whole options argument to build the params, not options.params, and updated my example accordingly.