I have a REST API, which returns User object, where its roles are specified via link to another object. So at localhost/project/api/users/27/ is JSON object:
{
"id": 42,
"name": "John",
"prijmeni": "Doe",
"login": "johndoe",
"nickname": null,
"grade": 1.3,
"roles": {
"href": "http://localhost/project/api/users/1716/roles/"
}
}
What I'm trying to do is to get roles in controller. My User service looks like this:
projectServices.factory('User', ['$resource', 'UserRoles',
function($resource, UserRoles, Role) {
var User = $resource('http://localhost/project/api/users/:id', {}, {
'query': {
method: 'GET',
isArray: true
}
});
return User;
}
]);
and I tried to add (to that resource code block):
User.prototype.roles= function(){
return UserRoles.get({id:42});
};
this one freezes browser when called in ngRepeat. So I tried
User.prototype.roles = UserRoles.get({id:42});
this works. Then I tried
User.prototype.roles = $resource(this.roles.href, {}, {
'get': {
method: 'GET',
isArray: true
}
});
says, that roles is undefined. I also tried to add transformResponse param to User service GET action, but that function was never called.
The second option works just perfectly fine - except, that I have to hardcode the user ID. Suitable solution would be with somehow getting the user ID for me (i tried this.id, but that didn't work).
Perfect solution would be creating resource from given href, but as I can't access roles in prototype, I don't know how.
Thanks for any advice.
This should do the trick
projectServices.factory('UserRoles', function(){
return $resource('http://localhost/project/api/users/:id', {id: #id},
{'query': {
method: 'GET',
isArray: true
})
}
Now you can call it with
UserRoles.get({id:42})
// this makes the request : http://localhost/project/api/users/42
The #id tells angular to use the id key from the parameter passed.
Related
I would like to ask if you know how could I duplicate parameters in a loopback REST connector query.
I have the following code:
details: {
'template': {
'method': 'GET',
'debug': true,
'url': 'https://www.example.com/data',
'timeout': 10000,
'headers': {
'Authorization': 'Bearer {token}'
},
'query': {
q: 'PHOTOS'
q: 'DETAILS',
id: '{id}'
},
'options': {
'useQuerystring': true
},
'responsePath': '$'
},
'functions': {
'searchData': [
'token',
'id'
]
}
}
The problem for that it is that it seems that loopback override the value of the parameter q by the last one, because I get only information for the last parameter.
Any idea how to solve it?
Thank you in avance.
You just have to pass them as an array:
'query': {
q: ['PHOTOS', 'DETAILS'],
id: '{id}'
},
Note that the options key, is passed to request and here's the documentation for useQuerystring:
useQuerystring - If true, use querystring to stringify and parse querystrings, otherwise use qs (default: false). Set this option
to true if you need arrays to be serialized as foo=bar&foo=baz
instead of the default foo[0]=bar&foo[1]=baz.
So if you remove it you'll end with something like ?q[0]=PHOTOS&q[1]=DETAILS.
You can also another option there:
qsStringifyOptions - object containing options to pass to the qs.stringify method.
Alternatively pass options to the
querystring.stringify
method using this format {sep:';', eq:':', options:{}}. For example,
to change the way arrays are converted to query strings using the qs
module pass the arrayFormat option with one of
indices|brackets|repeat
So you can actually end up with the same thing adding this:
"options": {
"qsStringifyOptions": {
"arrayFormat": "repeat"
}
}
And if you want to have just the brackets(something like this ?q[]=PHOTOS&q[]=DETAILS) you can specify brackets option:
"options": {
"qsStringifyOptions": {
"arrayFormat": "brackets"
}
}
I'm following along on the Angular JS Tutorial and I was wondering if there is an alternate approach to how I'm modifying it.
Currently, I am returning data with a factory that is defined as such:
angular.
module('core.card').
factory('Card', ['$resource',
function($resource){
return $resource('cards/:cardId.json', {}, {
query: {
method: 'GET',
params: {cardId: 'cards'},
isArray: true
}
});
}
]);
This is all good and working, as cards.json has all of the cards available and that's exactly what I want to return.
The method that they're describing, such as dealing with a RESTful service, assumes that there are multiple other specific JSON files that could get returned based on the route. I understand how to use that with an actual service, but let's say I wanted to alter the returned JSON data before it gets bound to my module so I don't have a bunch of extra data that I don't need?
Lets say /cards/foo.json contains something like this:
[{
"id": "foo",
"name": "Bar",
"img": "foobar.png",
"unnecessaryKey": "remove me"
}]
But where would I write a function that only returns:
[{
"id": "foo",
"name": "Bar",
"img": "foobar.png"
}]
Would I assign it in the same place where the query function is, such as:
...
return $resource('cards/:cardId.json', {}, {
query: {
method: 'GET',
params: {cardId: 'cards'},
isArray: true
},
alterReturnedData: {
// doStuffToFormatData
}
});
...
Or would it be best to just modify it in my Component as I'm doing now?
function alterReturnedData(data){
// doStuffToFormatData
}
var unmodified = Card.get({cardId:'foo'}, function(){
self.data = alterReturnedData(unmodified);
});
I just feel like it'd be better to return the data from the Service I actually want to the Component Controller instead of having a lot of logic in there to skew it around.
Is my approach OK to run this function in the Controller?
Or is it best to alter it in the Service, and how would I do so?
I have structure API:
api: {
users: {
details: {},
actions: {}
},
settings: {
users: {}
}
}
for example:
GET /api/users
return list of users
GET /api/users/1
return user with id 1
GET /api/users/1/details
return user deteils
GET /api/users/1/details/photo
return user fetail with alias photo
I wrote
.factory('userService', function($resource){
return $resource('/api/users/:id/:items/:itemsId', {}, {
query: { method: 'GET', isArray: false }
});
});
now I can do userService.query() and get list of users
but if I can`t do as this:
var users = userService.query();
users[1].name = 'newName';
users[1].save();
users[1] dont save edited info because users[1] dont have resourse methods, resourse methots isset only users.
And I can`t do as this:
var users = userService.query();
users[1].get('deteils');
How I can add resourse methods for all my structure?
First of all you have to prefix instance methods with a $ sign, like users[1].$save().
Then why are you setting isArray to false but treat the result like an array?
You should also bind the id paramter to the id property of your user. I assume it is called id.
So you would do something like this:
.factory('userService', function($resource){
return $resource('/api/users/:id/:items/:itemsId', { id: '#id' });
});
Note that you have to specify the parameters items and itemsId in your call, otherwise they will be left out.
e.g.:
var users = userService.query();
users[1].name = 'newName';
// assume users[1].id===1
users[1].$save();
// will do a POST /api/users/1
users[1].$get({ items: 'details' });
// will do a GET /api/users/1/details
users[1].$get({ items: 'details', itemsId: 'photo' });
// will do a GET /api/users/1/details/photo
PS: Maybe you should also check if the default methods (listed here) fits your REST API, otherwise you should define your own.
in bootstrap 2, I used the following code to post a json object,
$('#typeahead').typeahead({
source: function (query, process) {
var URL = 'http://localhost:8080/autocomplete/search/';
var query = {"t:jsonStringField": {
"name": "model",
"value": "fusion"
},
"t:jsonStringFilter": [
{"name": "year","value": "2009"},
{"name": "make","value": "ford"}
]
};
return $.getJSON(URL,
{ query: JSON.stringify(query)},
function (data) {
return process(data);
});
}
});
Now in twitter tyeahead 0.9.3 they have done away with the source concept and moved to a remote concept, but unfortunately I do no know how to work with it.
$(".typeahead").typeahead({
remote : {
url: 'http://localhost:8080/autocomplete/search/',
replace: function(uri, query) {
var query = {"t:jsonStringField": {
"name": "model",
"value": "fusion"
},
"t:jsonStringFilter": [
{"name": "year","value": "2009"},
{"name": "make","value": "ford"}
]
};
return uri + JSON.stringify(query);
},
filter: function(response) {
return response.matches;
}
return process(resultList);
}
}
Unfortunately it doesn't work, how do I just post the JSON object rather than appending it to the string? Thanks.
In your original code you use $.getJSON. This will send a request (and expects json as result) to: http://localhost:8080/autocomplete/search/?query=%7B%22t%3AjsonStringField%22%3A%7B%22name%22%3A%22model%22%2C%22value%22%3A%22fusion%22%7D%2C%22t%3AjsonStringFilter%22%3A%5B%7B%22name%22%3A%22year%22%2C%22value%22%3A%222009%22%7D%2C%7B%22name%22%3A%22make%22%2C%22value%22%3A%22ford%22%7D%5D%7D
To do the same for Twitter's Typeahead call your replace function of your remote data should return a valid url. In your function the ?query= part of the url is missing.
You will have to set: url: 'http://localhost:8080/autocomplete/search/?query=',
You also will have to urlencode you json input maybe.
Note: you will not need the line return process(resultList); You will have to use the filter function to convert your json results to valid data:
The individual units that compose datasets are called datums. The
canonical form of a datum is an object with a value property and a
tokens property.
you could use templates to style your dropdown results, see: http://twitter.github.io/typeahead.js/examples/
By default, the dropdown menu created by typeahead.js is going to look
ugly and you'll want to style it to ensure it fits into the theme of
your web page.
You will need the additional CSS from https://github.com/jharding/typeahead.js-bootstrap.css to style the default dropdown for Bootstrap.
I'm using an API that returns JSON data in this format:
{
paging: {
previous: null,
next: null
},
data: [
{ title: 'First Item' },
{ title: 'Second Item' },
...
]
}
I'm using Angular's $resource service to fetch this data.
My code - which is located in a controller - goes something like this:
var Entity = $resource('/api/entities');
var entities = $scope.entities = Entity.get();
And then, in the view, I can display the data like this:
<ul>
<li ng-repeat="entity in entities.data">{{entity.title}}</<li>
</ul>
It all works fine, but:
I'd rather expose only the contents of entities.data to the view, instead of the whole entities object. How can I intercept the data returned by the GET request to modify it before it populates $scope.entities?
Correlated question: since I am fetching an array of data, it would be cleaner to use Entity.query() instead of Entity.get(). But if I use Entity.query() in the code above, I get an error "TypeError: Object # has no method 'push'". This makes sense, since the API is returning an object instead of an array (hence, no 'push' method on the object). Again, if I could extract the .data attribute from the response, I'd have an array.
Following these indications by Dan Boyon, I managed to customize the default $resource service and to override the .get() or .query() methods, but I'm not sure where to go from there.
I don't think you need to modify the get or query defaults. Just use the success callback to do what you want. It should be more robust as well.
Entity.get(
{}, //params
function (data) { //success
$scope.entities = data.data;
},
function (data) { //failure
//error handling goes here
});
Html will be cleaner, too:
<ul>
<li ng-repeat="entity in entities">{{entity.title}}</<li>
</ul>
By the way, I usually declare services for my resources and then inject them into my controllers as I need them.
myServices.factory('Entity', ['$resource', function ($resource) {
return $resource('/api/entities', {}, {
});
}]);
You can use the Response Transformer (transformResponse) like this:
$resource('/api/entities', {}, {
query: {
method: 'GET',
responseType: 'json',
isArray: true,
transformResponse: function (response) {
return response.data;
}
}
});
This code modifies the "query" method behaviour, you can do the same for "get" ... !