JSON Expectations for Ember Data Polymorphic Relationships - json

DEBUG: Ember : 1.8.1
ember.js:15373DEBUG: Ember Data : 1.0.0-beta.18
ember.js:15373DEBUG: Handlebars : 1.3.0
ember.js:15373DEBUG: jQuery : 1.11.3
So I have a "Request" model which can depend on an account or other Requests that stand in for and account when it is fulfilled.
I'm a bit confused about how to get the JSON right for Ember to pick this up.
On the server-side, ActiveModelSeriailzers seems to want to serialize polymorphic relationships like this:
{
"id" : 1,
"account_id" : { "id": 1, "type": "account"}
}
I have the following ED Models:
// abstract-account.js
export default DS.Model.extend();
//account.js
export default AbstractAccount.extend();
//request.js
export default DS.Model.extend({
account: DS.belongsTo('abstractAccount', { polymorphic: true} )
});
When I try to load the record using the default json, I get
Error: Assertion Failed: You must include an `id` for account in an object passed to `push`
In my reading, I had gotten the impression that belongsTo polymorphic assocaitions should get sent like:
{
"id" : 1,
"account_id" : 1,
"account_type" : "Account"
}
If I change the server to serialize like that, I instead get
Uncaught Error: Assertion Failed: You may not pass `null` as id to the store's find method
I'm not really sure what I'm missing,or what the current recommended practice is.

I am using a similar setup with the first convention. Keep in mind Ember expects JSON objects to be namespaced under the class name, so for a GET to /requests you would need to return something like
{
'requests': [
{
'id': 1,
'account': { id: 10, type: 'account' }
},
{
'id': 2,
'account': { id: 20, type: 'some_other_abstract_account' }
}
]
}
This is with the RESTAdapter declared in application/adapter.js, I haven't tried the ActiveModelAdapter. I have seen documents advising another convention, but it's working for me with ember data 1.0.0-beta.16

Related

Can I create record in Ember without the record name

My Backend Rails API consumes POST requests in the form of
{
"name": "Mark White",
"email" : "mark#xyz.com"
}
Have tried this in Postman and this creates a record successfully.
In my ember frontend, Im using the following code to create a new record, from a form input:
app/routes/addUser.js
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
addUser(){
let formData = {
"name": this.controller.get('name'),
"email": this.controller.get('email')
};
let newUser = this.store.createRecord('user',formData);
newUser.save();
}
}
});
which generates the request as
{
"user": {
"name": "Mark White",
"email" : "mark#xyz.com"
}
}
which my backend is not prepared to handle and throws an error.
Any way I can quickly conform to the json format that works for me in Postman.
When the question is along the lines with the "How can I change my payload to server for it to be in a certain format?" then you should have a look at customizing a serializer.
But it looks like for solving your problem you might wanna take the standard JSON Serializer; in your app/serializers/application.js just type the following:
import JSONSerializer from '#ember-data/serializer/json';
export default class ApplicationSerializer extends JSONSerializer {
// ...
}
Note that it's for the Ember.js v4, according to your example you're using a bit older version so just adjust the syntax accordingly.

This Ember belongsTo relationship is working, but I can't see how

I've been developing an app for about a year now, so I started it mid-2014 and have been upgrading ember.js and ember-cli as things move forward on those projects. I'm at Ember 1.11 now.
EDIT: Application Adapter
var ApplicationAdapter = DS.RESTAdapter.extend( {
namespace: 'api',
host: null,
setHost: Ember.on('init', function() {
set(this, 'host', this.container._registry.resolve('config:environment').API_ENDPOINT);
})
});
export default ApplicationAdapter;
My JSON API returns a main projects object, along with other sideloaded objects (like projectStatus). What I can't understand is, since I don't have any adapters or serializers that specify this, how I'm it's able to use the returned JSON, because it looks like this:
{
"projects" : {
"id": 4462875
"projectName" : "New business from Acme",
"projectDescription" : "Just another great project",
"pmlinks" : [ 1, 2],
"statusLinks" : [ 1440 ],
"commentsLinks" : [ 39 ]
},
"projectResources" : [ {
"id" : 1,
"name" : "Wile E. Coyote"
}, {
"id" : 2,
"name" : "Roadrunner"
}],
"projectComments" : [ {
"id" : 39,
"projectComment" : "started the project",
} ],
"projectStatuses" : [ {
"id" : 1440,
"status" : "G",
"trending" : "N",
"comment" : null,
"lastModifiedDate" : "2015-07-17T13:46:11.037+0000",
"project" : 4462875
} ],
}
I can't find anything in the Ember docs that recommend this "*Links" format for the relationships, and in fact it suggests using something more like status_ids. But the example it shows doesn't use _ids so I'm even more confused.
Here's a snippet of my project model:
statusUpdates: DS.hasMany('projectStatus'),
projectComments: DS.hasMany('projectComment'),
projectResources: DS.hasMany('projectResource'),
What I'm trying to figure out is with my new belongsTo relationship to schedule, how should the JSON be formatted from the API? It seems to work if the project object has a property like this "scheduleLinks": [10] but not if it's like "schedule": 10 or "schedule_id": 10 and that seems to be what the documentation says should work.
EDIT:
Maybe it's because the other objects like projectComments are named the way my model expects, and they're all returned at the same time from one API, that it doesn't even matter what the properties in the projects object is? Is that only to look up relationships if they're not all sideloaded?
The sideloading definitely is the thing here. Because if I change "statusLinks" to "projectStatus" then, since I have that relationship established in the project model, it will try to hit the API for that, e.g. /api/projectstatuses/:id.
So what seems to be happening is that the relationship is being hooked up from the belongsTo side implicitly; if the data is sideloaded, I don't need any links or IDs on the main object.

Incorrect JSON format for Ember Data

I'm using the latest version of EmberJS and Ember Data.
I have the next JSON data:
[{
"id": 6,
"name": "First object",
"vol": 40,
"description": "bla bla bla",
"category": "first"
}, {
"id": 7,
"name": "Second object",
"vol": 17,
"description": "Some description",
"category": "second"
}]
And the next model:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
vol: DS.attr('number'),
description: DS.attr('string'),
category: DS.attr('string')
});
And I don't understand how to make Ember data works. I have suspicious that ember data expects something like next:
[{
model-name {
"id": 6,
"name": "First object",
"vol": 40,
"description": "bla bla bla",
"category": "first"
}
}, {
model-name {
"id": 7,
"name": "Second object",
"vol": 17,
"description": "Some description",
"category": "second"
}
}]
In console I have a bunch of warnings:
WARNING: Encountered "0" in payload, but no model was found for model
name "0" (resolved model name using emdber-drink-
it#serializer:application:.modelNameFromPayloadKey("0"))
WARNING: Encountered "1" in payload, but no model was found for model
name "1" (resolved model name using emdber-drink-
it#serializer:application:.modelNameFromPayloadKey("1"))
And more than hundred similar records (that is how many records server returns on request).
And no data in store.
How can I fix this problem without changing JSON that I receive from server?
From http://emberjs.com/api/data/classes/DS.JSONSerializer.html
In Ember Data a Serializer is used to serialize and deserialize
records when they are transferred in and out of an external source.
This process involves normalizing property names, transforming
attribute values and serializing relationships.
At this time there are 4 types of serializers:
DS.Serializer
DS.JSONSerializer
DS.RESTSerializer
DS.JSONAPISerializer
The JSON data you have is plain json.
JSONSerializer is useful for simpler or legacy backends that may not
support the http://jsonapi.org/ spec
So you need JSONSerializer which is the most simple solution to consume plain json data.
With ember-cli is pretty easy create a new serializer, for example for a model book:
ember generate serializer book
which will produce something like this:
version: 1.13.8
installing serializer
create app/serializers/book.js
installing serializer-test
create tests/unit/serializers/book-test.js
The previous command will create a serializer of type RESTSerializer. (If you dont use ember -cli just create this file at hand)
// app/serializers/book.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
});
and then change RESTSerializer by JSONSerializer:
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
primaryKey: '_id', // useful for mongodb-like data
});
I hope you enjoy learning ember as much as I do.
You should be able to add a custom serializer to add a root key to your JSON data. I haven't tested this but you could try something like:
(using a "book" model as an example)
// app/serializers/book.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
extractArray: function(store, type, payload) {
var newPayload = {};
newPayload[type.modelName] = payload;
return this._super(store, type, newPayload);
}
});
If this is a problem with the entire API, then you might need to make a custom serializer for the entire application and use the passed-in type argument to build the root keys.
Also as a side note, I probably wouldn't use Ember Data if I didn't have control over the API. Unless it follows strict guidelines like jsonapi.org, you're bound to have a few headaches with it.
EDIT - changed answer to the suggestion above (using 'type' to build root key).
I took another approach and overloaded the 'modelNameFromPayloadKey' method.
If the payload key is of type integer, it will be mapped to the name of the model instead.
// app/serializers/<model>.js
import DS from 'ember-data';
import normalizeModelName from "ember-data/-private/system/normalize-model-name";
import {singularize} from "ember-inflector";
export default DS.RESTSerializer.extend({
model: '<model>',
modelNameFromPayloadKey(key) {
// Check whether the key is of type integer
if (!isNaN(key)) {
return normalizeModelName(this.get('model'));
}
return singularize(normalizeModelName(key));
},
});

ember 1.12 findAll issue using rest, pod model over json response

I have a Rest adapter right configured and when I try:
router.js
export default Ember.Route.extend({
model: function() {
return this.store.find('main.user');
}
});
I got this:
WARNING: Encountered "main.user" in payload, but no model was found
for model name "mainUser" (resolved model name using
plezure#serializer:-rest:.typeForRoot("main.user"))
Error while processing route:
main.user Assertion Failed: The response from a findAll must be an
Array, not undefined Error: Assertion Failed: The response from a
findAll must be an Array, not undefined
My Json return:
{
"main.user": [
{ "id": "1", "name": "foo" },
{ "id": "2", "name": "bar" }
]
}
model.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string')
});
My pod are right configured, everything is fine, all works if I change to the find to a non-pod model, like this (model exists at main/):
export default Ember.Route.extend({
model: function() {
return this.store.find('main');
}
});
But with 'main.user' ember is uncapable to deal with json return, using fixtures pod names works well, it's ony happens on json response.
Anyone have any ideia about this kind of issue?

How to set the `type` property of the serialized JSON object in Ember

I have a model named line-item in my ember application. When I try to create a line-item record, I observe in the network tab that ember serialises a JSON object that looks like so:
{
data: {
quantity: 1,
type: "lineItems", // note this
links: {
cart: {linkage: {id: "7", type: "carts"}}
product: {linkage: {id: "a65874aa87b", type: "products"}}
}
}
}
What I would like to do is change the value of the type property to line-items instead (since the backend expects that). I have tried overriding the serializeIntoHash and typeForRoot methods of the RESTSerializer class, but no luck there. I'm probably mucking around with the wrong methods, but I'm just not sure where I should be looking at instead.
I would really appreciate some pointers in the right direction.Thanks!