I have following resources
Question /api/v1/questions/{questionID}
Category /api/v1/categories/{questionID}
Comment /api/v1/comments/{questionID}
Recommendations /api/v1/recommendations/{questionID}
Pattern answers /api/v1/patternanswers/{questionID}
The API is made to deliver children objects by parent object ID, and I don't see that compatible with Ember-data out-of-the-box (or without hacking, only proper configuration).
Question resource gets me JSON like this:
{
"errors": [],
"data": {
"created": 1439832769693,
"updated": 1440012378723,
"userID": 20,
"user": {
"password": null,
"created": null,
"updated": null,
"photoID": null,
"photo": null,
"email": "notsoimportant#host.com",
"emailConfirmed": false,
"phoneNumber": null,
"phoneNumberConfirmed": false,
"accessFailedCount": 0,
"id": 20,
"userName": "qwerty"
},
"categories": [], // does not exist in json
"addCategoriesIDs": [],
"removeCategoriesIDs": [],
"recommendations": [],
"removeRecommendstionIDs": [],
"patternAnswers": [],
"removePatternAnswerIDs": [],
"comments": [], //does not exist in json
"hint": null,
"version": 1,
"commonVersion": 8,
"id": 7,
"questionText": "What is your name?",
"weight": 0,
"answerType": 0,
"status": 0,
"estimatedTime": null,
"private": false
}
}
Properties "patternAnswers", "recommendations" are in the question, but "comments" and "categories" are not.
More over I don't get Question with listed properties filled with IDs of dependent objects. I wish I could get
{
"errors": [],
"data": {
"comments" : [11,12,13,14],
"categories" : [21,22,23,24],
"recommendations": [1,2,3,4],
"removeRecommendstionIDs": [],
"patternAnswers": [5,6,7,8],
"removePatternAnswerIDs": [],
"hint": null,
"version": 1,
"commonVersion": 8,
"id": 7,
....
}
}
.. but they are empty :(
I would like to configure my Question Model and Question Serializer to grab all that dependent objects asynchronously.
I would like to get everything just issuing
var QuestionModel = this.store.find('question',7);
var comments = QuestionModel.get('comments');
var categories= QuestionModel.get('categories');
var recommendations= QuestionModel.get('recommendations');
var patternAnswers= QuestionModel.get('patternAnswers');
How is it feasible?
If you want this to work out-of-the-box, you should set up your API according to ember guides (http://guides.emberjs.com/v2.0.0/models/the-rest-adapter/). That said, endpoints must be:
Question /api/v1/questions/{questionID}
Category /api/v1/categories/{categoryID}
Comment /api/v1/comments/{commentID}
Recommendations /api/v1/recommendations/{recommendationID}
Pattern answers /api/v1/patternanswers/{patternanswerID}
And payload from GET /api/v1/questions/{questionID} should look like:
{
"question": {
"categories": [], // an array of ids
"recommendations": [], // an array of ids
"patternAnswers": [], // an array of ids
"comments": [], // an array of ids
"id": Number, // question id
... // others
}
}
Otherwise, you need to change the default behavior of DS.Model or/and RESTAdapter, or use QuestionController with attached models of Category, Comment, etc. Or you can pack all things in model and setupController hooks of route represented that data.
I suggest you to answer yourself these questions:
Do you really need such a complex model at the client side? The only reason you might need model is the requirement to create/change data in that set all at once.
Can you change the API? It's better to change API in regard to REST specifications, to not maintain spaghetti at client side.
How far you can go with changing the API? There is another but similar scheme:
Question /api/v1/questions/{questionID}
Category /api/v1/questions/{questionID}/categories/{categoryID}
Categories /api/v1/questions/{questionID}/categories
It's a bit easier to configure in Ember then your version.
Related
I'm trying to create record with associated records in one request. In case if some of nested records have validation errors, I'd like to access appropriate errors on that record. I'm using json-api adaptor, so what should be the format of errors from the backend? I'm trying something like this, with no luck though:
{"errors":[
{
"detail": "can't be blank",
"source": {
"pointer":"data/relationships/steps/0/data/attributes/est_threshold"
}
}
]}
According to this line, it should be implemented somehow:
https://github.com/emberjs/data/blob/master/addon/adapters/errors.js#L7
Any ideas?
You'll need to sideload nested records in the data. The example structure given in the ember guides is:
{
"post": {
"id": 1,
"title": "Node is not omakase",
"comments": [1, 2, 3]
},
"comments": [{
"id": 1,
"body": "But is it _lightweight_ omakase?"
},
{
"id": 2,
"body": "I for one welcome our new omakase overlords"
},
{
"id": 3,
"body": "Put me on the fast track to a delicious dinner"
}]
}
https://guides.emberjs.com/v1.10.0/models/the-rest-adapter/
So it doesn't seem to be implemented yet. I found kinda hackish way to do that in the model mixin:
`import Ember from 'ember'`
RelatedErrors = Ember.Mixin.create
save: ->
#_super().catch (resp) =>
resp.errors.forEach (err) =>
if [_, rel, idx, attr] = err.source.pointer.match /^data\/relationships\/(\w+)\/(\d+)\/data\/attributes\/(\w+)$/
#get(rel).objectAt(idx).get('errors').add(attr, err.detail)
`export default RelatedErrors`
However, add on DS.Errors is deprecated, so this is still not a perfect solution. Also invalid state of related models need to be cleared before each commit, which is not happening this far.
With Ember Data and Jsonapi. How is a json paginated resource supposed to look like?
I built my response so it looks like:
"meta": {
"page": {
"number": 1,
"size": 5,
"total": 39
}
},
"links": {
"self": "http://localhost:3099/api/v1/articles",
"prev": null,
"next": "http://localhost:3099/api/v1/articles?page[number]=2",
"first": "http://localhost:3099/api/v1/articles?page[number]=1",
"last": "http://localhost:3099/api/v1/articles?page[number]=39"
},
"data": [
...
]
But I am not exactly sure if this is the right format. based on the explanation at http://jsonapi.org/format/#fetching-pagination
Or, are the pagination links (i.e. prev, next, first and last) supposed to be in meta.page ?
You could use ember-cli-pagination and its format to do pagination. I'm pretty sure Ember Data does not follow the JSON API spec strictly.
Based on your sample this could be a format:
{
"meta": {
"total_pages": 3,
"page": 1
},
"articles": [
{"id": 1, "title": "Hello World", "body": "More to Come"},
// ......
]
}
The request URL of this payload could be http://localhost:3099/api/v1/articles?page=1. See the API for more info.
Ember Data doesn't follow the JSON spec strictly so you should concentrate more on setting up the JSON with what ED needs. I would personally move the 'links' info into the meta tag. Otherwise Ember-Data will attempt to put them into a model called 'links', which may not be what you want. If you do intend to store those inside a separate 'links' model, then what you have is fine.
we would like to add lazy loading functionality to our ember project, but it looks like ember will always override fields not returned by the response JSON with NULL. First I get a list of users:
GET https://example.com/users
{
"users": [
{
"id": 1,
"name": 'user1',
"email": 'email#user1.com',
"images": [],
"posts": []
},
{
"id": 2,
"name": 'user2',
"email": 'email#user2.com',
"images": [],
"posts": []
},
{
"id": 3,
"name": 'user3',
"email": 'email#user3.com',
"images": [],
"posts": []
},
]
}
This provides a minimal set of user information, with two empty hasMany relations "images" and "posts".
Now, if somebody want to see the users posts or images he would click a button which triggers the lazy loading:
GET https://example.com/userImages/1
{
"user": {
"id": 1,
"images": [1,2,3,4]
},
"images": [
{
"id": 1,
"name": "image1",
"path" "path/to/img1/"
},
{
"id": 2,
"name": "image2",
"path" "path/to/img2/"
},
{
"id": 3,
"name": "image3",
"path" "path/to/img3/"
},
{
"id": 4,
"name": "image4",
"path" "path/to/img4/"
}
]
}
To reduce traffic to a minimum, only the newly loaded information is included. After the adapter has deserialzed and pushed the new data to the store, all fields from User1 which are not included in the payload (name, email) are set to NULL in the ember store (tested with store.pushPayload('model', payload)).
Is there a possibility to update only incoming data? Or is there a common best practice to handle such a case?
Thanks in advance for your help
EDIT:
One possibility would be to extend the ember-data "_load()" function with the block
for (var key in record._data) {
var property = record._data[key];
if( typeof(data[key]) == 'object' && data[key] == null ) {
data[key] = property;
}
}
But this is the worst possible solution I can imagine.
I think what you want is the store's update method. It's like push (or pushPayload), except that it only updates the data that you give it.
Your property returns a promise and that promise returns whatever came back from the server.
foobar: function() {
return this.store.find('foobar');
}
When the promise resolves, you have two versions of the data, the one already rendered in the client (dataOld) and the one that just returned from the backend (dataNew). To update the client without removing what hasn't change, you have to merge the old and the new. Something along the lines of:
foobar: function() {
var dataOld = this.get('foobar');
return this.store.find('foobar').then(function(dataNew) {
return Ember.$.merge(dataOld, dataNew);
});
}
I am working with Web Api and Ember JS, all worked well until i started using Ember Data, which seems to need the object name included in the json.
How can i add this?
Currently getting :
[
{
"id": 1,
"title": "maxima",
"subTitle": null,
"description": "Maxima de boot",
"image1": null,
"image2": null,
"active": false,
"skipper": null
},
{
"id": 2,
"title": "beatrix",
"subTitle": null,
"description": "Beatrix de boot",
"image1": null,
"image2": null,
"active": false,
"skipper": null
}
]
Thanks so much!
Having worked with Web API and Ember Data plenty you will find it easier to fix the json client side using a serializer. Assuming you're finding posts, `this.store.find('post') you would create a custom serializer for it:
App.PostSerializer = DS.RESTSerializer.extend({
extractArray: function(store, type, payload) {
payload = {posts: payload};
return this._super(store, type, payload);
}
});
Example: http://emberjs.jsbin.com/OxIDiVU/623/edit
Read more about it (and other methods) here http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_extractArray and here https://github.com/emberjs/data/blob/master/TRANSITION.md
Ember data doesn't know how to map that data, your json should look like this instead.
{ blogs: [
{ "id": 1, "title": "maxima", "subTitle": null, "description": "Maxima de boot", "image1": null, "image2": null, "active": false, "skipper": null },
{ "id": 2, "title": "beatrix", "subTitle": null, "description": "Beatrix de boot", "image1": null, "image2": null, "active": false, "skipper": null } ]
}
Now ember knows that the objects from the server, should be mapped to the blogs model.
I'm pretty new at this and haven't worked with Ember before, but... Instead of returning an array of "stuff" objects, can you not modify the code to return an object that internally just contains an array of "stuff" objects? Then, I expect the default json serializer should take care of this for you.
By default, when using django-tastypie and fetching a resource list, the response is of the format:
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 3
},
"objects": [{
"body": "Welcome to my blog!",
"id": "1",
"pub_date": "2011-05-20T00:46:38",
"resource_uri": "/api/v1/entry/1/",
"slug": "first-post",
"title": "First Post",
"user": "/api/v1/user/1/"
},
...
]
}
I've dug into the documentation and looked & looked, but I can't seem to find any kind of meta option or setting to change the "objects" key to actually describe the returned items. For example, let's say I have list of locations in one api call and a list of people in another. I'd like to be able to differentiate the key to "locations" and "people". The real reason for this is because I'm using RestKit on iOS and want to be able to set up multiple mappings.
The Resource hooks alter_* can be used to alter the structure of the data.
An example Resource using 'locations' would be:
class MyLocationsResource(ModelResource):
def alter_list_data_to_serialize(self, request, data):
data['locations'] = data['objects']
del data['objects']
return data
def alter_deserialized_list_data(self, request, data):
data['objects'] = data['locations']
del data['locations']
return data