I am trying to use the JSON API Adapter with ember-cli 2.5.1 , but I'm having a bit of trouble.
I have a todo-list.js model, which has a "hasMany" relationship to todo-list-item.js. Getting the todo-list, the server returns this:
{
"links": {
"self": "http://localhost:4200/service/v1/todolists/b-tlst-af69786c-cbaf-4df9-a4a3-d8232677006a"
},
"data": {
"type": "todo-list",
"id": "b-tlst-af69786c-cbaf-4df9-a4a3-d8232677006a",
"attributes": {
"name": "b1-TodoList",
"created-on": 1468474962458,
"modified-on": 1468474962458
},
"relationships": {
"todolistitems": {
"data": {
"type": "todo-list-item",
"id": "b-todo-b5e3c146-d93a-4f97-8540-875bbcd156ca"
}
}
}
}
}
If there had been two TodoListItem children instead of one, the value of that "data" key would have been an array, rather than an object.
After receiving this, I was expecting the Ember Chrome plug-in's "Data" tab to show 1 TodoList and 1 child TodoListItem. Instead, it shows 1 TodoList and 0 TodoListItems.
I note from the Network tab that the browser never makes a request to get the items listed in the "data" section of the response.
Is the relationships section above correct and sufficient?
It turns out to have been caused by promise misunderstandings on the client side, and additionally, on the server I had to put dashes in the "relationships" key (i.e. "todo-list-items") and make the value of "data" an array.
Related
I have a fairly specific problem that I was hoping one of you really intelligent folk might know a solution for (or even a workaround at this stage)
Specifically, I'm dealing with action cables, nested JSON, and the JSONAPI.
I have an asset model, which has some attributes like name, desc etc. but it also has an attribute called state which is a complex nested JSON object.
// app/models/asset.js
export default DS.Model.extend({
// Attributes
name: DS.attr('string'),
desc: DS.attr('string'),
state: DS.attr(),
lastSeen: DS.attr('date'),
});
When anything on the asset changes in the backend, it is pushed down the cable to Ember, where it does a pushPayload(data), the payload looks like this;
{
"data": {
"id": "5",
"type": "assets",
"attributes": {
"asset_id": "962ABC",
"name": "962 ABC",
"desc": "Test Vehicle",
"activation_status": "active",
"state": {
"avl": {
"longitude": 152.9475426,
"reported_at": "2017-06-22T21:59:52Z"
},
"dfm": {
"in_alarm": false,
"reported_at": "2017-06-21T05:46:57Z",
"sensitivity": "normal",
"voice_prompt": false,
"driver_detected": true,
},
"tpms": {
"system_desc": "123ABC",
"system_type": "123_abc"
}
},
"last_seen": "2017-06-22T21:59:54.000Z"
},
"relationships": {
"company": {
"data": {
"id": "1",
"type": "companies"
}
},
"events": {
"links": {
"related": "/events/?asset_id=5"
}
},
"messages": {
"links": {
"related": "/messages/?asset_id=5"
}
}
}
}
}
This all works fine and dandy, updates to the asset & state are displayed as they happen thanks to the cable, and state is read only so I don't have to worry about saving anything. HOWEVER, I have noticed that when any single attribute on state changes in the backend, the entire asset is pushed down from the backend (this should be fine), and then this fires the observer for state and also all observers for state descendants - whereas I need it to only fire the observer for the state attribute that changed.
I have tried a number of things and each seemed to either not work at all, or still continue to update state in a way that fired off all state observers.
What I have tried;
ember-model-data-fragments (while it should work, I think the way that the action cable pushes the data must subvert this?)
embedded records (requires an ID for state, not currently compatible with JSONAPI)
raw json transform (making the json into ember objects, didn't seem to help)
Can anyone suggest a strategy or solution for me to try? I've spent nearly 2 days on this problem.. I would even settle for just splitting it up between avl/tpms/dfm, as long as when an attribute in one of those sections is changed, it doesn't notify properties from the other 2 sections.
Thanks
I am new to Ember and I will like to implement a server side search endpoint that returns results for several models/tables in DB.
Trying to figure out what is the best practice to do this in Ember.
Server results can contain several types of objects/models, each model is already defined in ember-data.
So far I am able to retrieve the results (I can see the results in ember-inspector) but I am not able to render it properly (I only have access to first object for some reason).
Current architecture:
rails-5 server with JSONAPI response
ember 2.3 with multiple models
Flow:
POST /search with body:
{"search":{"query": "xxx","size": "10"}}
RESULT:
{
"data": [
{
"id": "111111",
"type": "foo",
"attributes": {
"x": "y"
}
},
{
"id": "222222",
"type": "bar",
"attributes": {
"z": "y"
}
}
]
}
I am trying to create an organisations-table component, which will display a list of organisations, and the user for which each organisation belongs. I pass the model for the organisations into the component via an organisations.hbs template, which results in the following response from the server:
{
"organisations": [
{
"id": 0,
"name": "Org0",
"user": 1
},
{
"id": 1,
"name": "Org1",
"user": 2
},
{
"id": 2,
"name": "Org2",
"user": 2
}
]
}
In order to display the username for each user, the component then makes its own call to the server querying against the id for each of the users.
Is this the correct approach? My understanding is that components are supposed to be isolated by design, only aware of the data passed into them, but in this example, the component is sending its own requests to the server for the additional data.
I have created this ember twiddle to hopefully give an idea of the structure of the app (comments welcome!).
Thanks in advance
The component itself has nothing to do with the calls, { async: true } means that the relationship won't be fetched unless it is "needed".
Needed in this case being the organisation.user.username in your component's template.
Keep in mind that model in your case is an array of DS.Model objects that have relationships.
I'm working on a web service API using the HATEOAS REST representation.
My client can create an item (e.g. a stub of a blogpost):
POST /item
204 Created
Content-Type: application/vnd.foo.item+json
{
"id": 42,
"title": "Lorem Ipsum",
"status": "STUB",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"activate": {
"href": "/item/42/activate"
},
}
}
After that the client can activate the item following the activate link (e.g. go live with the post). So it makes another call to the API:
POST /item/42/activate
200 Ok
Content-Type: application/vnd.foo.item+json
{
"id": 42,
"title": "Lorem Ipsum",
"status": "ACTIVE",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"permalink": {
"href": "/item/42/permalink"
}
}
}
Up to here it is fine. But the problem is that I'm looking for a way to tell the client a suggestion about the next action to do (it's backend business logic).
In my case could be:
Bring the user to post page following the permalink
Bring the user to a shop cart to buy post extra features (visibility, more images, homepage positions and so on...)
Tell the user that the post is pending content review
I don't have an idea on how I could encapsulate this information in HATEOAS.
I was thinking to something like:
POST /item/42/activate
200 Ok
Content-Type: application/json
{
"suggested-action": "check-censure-panel",
"censure-reason": "censored (gambling)",
"_embedded": {
"foo.item": {
"id": 42,
"title": "Lorem Ipsum",
"status": "ACTIVE",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"permalink": {
"href": "/item/42/permalink"
}
}
}
}
But the problem is that every suggested action is heterogeneous for extra attributes, another example may be:
"suggested-action": "go-to-checkout",
"product-order": 424242100,
They don't have a common interface, so I can't make a a vnd.foo.suggestedAction+json type.
What is the best way to design this response?
The next action is a state transition, and you seem to be using HAL so any state transitions should be presented as HAL.
Clients of your app need to react to what state transitions your app provides. So one very simple thing you could do is send a Location header to the next resource the app should present. You could even 302 redirect them there instead of 200'ing them with the updated resource.
You could provide the next action as a link...and not necessarily a HAL link. You could do it as a Link header (https://www.rfc-editor.org/rfc/rfc5988) but i think that would be weird, i just bring it up to knock home the point that your app needs to tell your client about a link.
You seem to want to use custom media types, but you could use profile links (https://www.rfc-editor.org/rfc/rfc6906) and mix in a profile into your vnd.foo type. You can stick to your vnd.foo type and just have it defined that there is an optional suggested-action link relationship. The problem in your example is you're defining it with data fields, but use a link:
{
"id": 42,
"title": "Lorem Ipsum",
"status": "ACTIVE",
"body": "Very long text."
"_links": {
"self": {
"href": "/item/42"
},
"permalink": {
"href": "/item/42/permalink"
},
"x:suggested-action" : {
"href" : "/path/to/best/action"
}
}
the client can follow that link, present the user with an option to follow that link, or ignore it. In the middle case, it's nice if your app provides some context to the user, like a title field:
"x:suggested-action" : {
"href" : "http://path/to/check/censure/panel",
"title" : "Check Censure Panel"
}
Also you can give a hint as to the resource the app can expect:
"x:suggested-action" : {
"href" : "http://path/to/check/censure/panel",
"title" : "Check Censure Panel",
"type" : "vnd.censure.panel/json"
}
I personally don't like doing that as i like my client to react to whatever i send them, but it's useful when you give multiple suggested actions:
"x:suggested-action" : [
{
"href" : "http://path/to/check/censure/panel",
"title" : "Check Censure Panel",
"type" : "vnd.censure.panel/json"
},
{
"href" : "http://path/to/checkout",
"title" : "Start Checkout",
"type" : "vnd.checkout/json"
}
]
now the app can decide based on well defined media types which of the suggested actions it wants to do, present, or ignore.
I use side-loading extensively in my Ember Data app to avoid making multiple round-trips to the API, and until now it has always worked out great.
I am using Ember 1.6.0-beta.3, Ember Data 1.0.0-beta.7, DS.ActiveModelAdapter, and DS.ActiveModelSerializer.
If I save a Foo model, which causes this request:
POST /foos
{
"foo": {
"name": "Foo"
}
}
The server responds with the created Foo, but also with a side-loaded Bar that references the new Foo.
201 Created
{
"foo": {
"id": 1,
"name": "Foo"
},
"bars": {
"id": 1,
"foo_ids": [1]
}
}
After this response, it appears that the Bar does not know that a Foo with ID=1 is in the store, and issues the following request:
GET /foos?ids[]=1
This request fails because I don't have that endpoint. I would not expect that Ember Data should send this request at all, because we just loaded the Foo with ID=1.
Instead, I would expect that no additional request should be made. Instead, the Bar should become associated with the newly created Foo.
Is my expectation out of line? Is this a bug?
EDIT
I was able to work around this issue by explicitly side-loading the foo. This is very inelegant, but hey, it's a work-around.
Specifically, I changed my API to respond with the following after a POST /foos:
{
"foo": {
"id": 1,
"name": "Foo"
},
"bars": {
"id": 1,
"foo_ids": [1]
},
"foos": [{
"id": 1,
"name": "Foo"
}]
}
Then the side-loaded Bar doesn't try to load it's Foo.
I'm going to leave this open, because a work-around is not a fix.