Save belongsTo change in model not part of Payload - ember.js

I'm trying to save a change to a parent model on a child model in Ember to my server, but for some reason the REST Payload doesn't contain the belongsTo Relationship
My 2 models are defined like this:
parent.js
import DS from 'ember-data';
export default DS.Model.extend({
parentName: DS.attr('string')
});
child.js
import DS from 'ember-data';
export default DS.Model.extend({
childName: DS.attr('string'),
parent: DS.belongsTo('parent')
});
when I change the belongsTo to a different parent for a childModel by editing and saving an existing record. Somehow my payload doesnt include the parent model.
For example.
I have a child like this:
{
id: 1,
parent: 1,
childName: "Child 1"
}
and in code I do this:
childModel.set('parent', parentModel); // this is a different parentModel, one with id: 2
I would expect the payload to look something like this:
HTTP-PUT: http://server/child/1
{
id: 1,
parent: 2,
childName: "Child 1"
}
however, in reality, the payload is this:
HTTP-PUT: http://server/child/1
{
id: 1,
childName: "Child 1"
}
What goes wrong here? Why is the parent relationship missing from the payload?
Some extra information:
Ember v2.0.1
Ember-data v2.0.0
Relationships must be async: true (which is the default)
I'm using a standard DS.JSONAPISerializer in combination with the DS.RestAdapter

Turns out during the serialization, I only extracted the attributes hash from my JSON Api - JSON, Obviously I need to look for relationships in the relationships hash.

Related

Ember data subdocuments

Other questions on this topic are over 3 years old.
I see this documentation on serializers, which seems like the answer, but I can't get it to work. I just want a simple JSON object structure like this:
business-hours : {
monday: {
open: “24hr”,
from: <some time>,
to: <some time>
},
tuesday: {
open: “closed”,
from: <some time>,
to: <some time>
}
},
What's the move here?
Edit: as for editing keys in an empty DS.attr() field, in response to locks' request for more information. I pass this component the model:
<h3>Business Hours</h3>
{{business-hours model=model}}
And attempt to edit the model.businessHours attribute in order to create this ideal record outlined above:
{{input type="time" value=model.businessHours.friday.from}}
{{input type="time" value=model.businessHours.friday.to}}
Should this type of syntax be allowing me to form the appropriate 'subdocument' in the model? With the empty DS.attr() field?
If you want to edit these subdocuments you should use an model for this.
So for this you have two models:
/models/business-hour-week.js
import DS from 'ember-data';
export default DS.Model.extend({
monday: DS.hasMany('business-hour-day'),
tuesday: DS.hasMany('business-hour-day')
});
/models/business-hour-day
import DS from 'ember-data';
export default DS.Model.extend({
open: DS.attr('string'),
from: DS.attr('date'),
to: DS.attr('date')
});
And then use a serializer to convert your data to a valid JSONAPI Document like this:
{
data: {
type: 'business-hour-week',
id: '1',
relationshops: {
monday: {
data: {
id: '1',
open: '24hr',
from: '<some time>',
to: '<some time>',
}
},
toesday: {
data: {
id: '1',
open: 'closed',
from: '<some time>',
to: '<some time>',
}
}
}
}
}
Then you can use this and work with it as expected.
None of the above worked for me. Using a serializer to add the ids seems hackery. This is one of those things that should be easy... These are my two solutions to the problem.
This is the easiest. You can use an addon called ember fragments: https://github.com/lytics/ember-data-model-fragments. This allows you to create nested models, without forcing child records to have an id. Fragment models are set up just like normal ones, and have clean/dirty attrs.
You can set up the 2 models as described above (week/days), but you would use a belongsTo, instead of a hasMany (single record vs array of records.) You would also have to create serializers for each parent model (week, and whatever parent it lies under) and use the embedded records mixin, setting the relationship attrs to "deserialize: records, serialize: records". You just have to make sure each models response from the server has its own id in the hierarchy. If you are using Mongo/Mongoose, you can create the sub-docs by nesting child schemas, vs using object literal syntax. This way each sub-doc gets it's own id, even if it's a one-to-one relationship.

ember data not saving foreign key, sent as null

My ember app is not sending my foreign key to the back-end.
I have a table called issues which is has a related table called categories
My model is:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
category_id: DS.belongsTo('category'),
description: DS.attr('string')
});
My route is:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
return this.store.findAll('issue');
},
actions: {
create: function(){
var issue = this.store.createRecord('issue');
issue.name = this.get('controller').get('newName');
issue.description = this.get('controller').get('newDescription');
issue.category_id = parseInt(this.get('controller').get('newCategory'));
//debugger;
console.log(issue);
issue.save();
},
...
other actions
...
}
}
});
the console.log from above looks like the category_id is getting set correctly:
category_id: 3
description: "foobar"
name: "test"
However my JSON payload that gets sent to the backend looks like:
{"issue":{"name":"test","description":"foobar","category_id":null}}
I tried stepping through by adding a custom serialiser in app/serializers/application.js
export default DS.RESTSerializer.extend({
...
serialize: function(snapshot,options){
console.debug('options='+options);
debugger;
var json = this._super(snapshot, options);;
return json;
}
...
});
But I got lost in all the super calling super indirection.
The snapshot.record has category_id: 3, but the json coming back from the this._super() call has category_id: null
options has includeID:true
Any clues will be much appreciated ...
Ember : 2.0.2
Ember Data : 2.0.0
Your model definition is wrong, when dealing with relationships you define them just as you would define any other attribute, there is no need to use _id.
export default DS.Model.extend({
name: DS.attr('string'),
category: DS.belongsTo('category'),
description: DS.attr('string')
});
As for the creation you should always use setters/getters when dealing with ember objects:
create: function() {
var issue = this.store.createRecord('issue', {
name: this.get('controller').get('newName'),
description: this.get('controller').get('newDescription'),
category: this.get('controller').get('newCategory') // assuming new category is a DS.Model instance of category
});
issue.save();
}
If you wish to stick to the syntax you have you would use issue.set('name', this.get('controller').get('newName')), from the looks of your code it seems you are going about this in the wrong way.
You should have a this.route('new') nested under your issues route, that way you wouldn't have to use the controller to store information.
You would simply set the model of the new route to:
model: function() {
return this.store.createRecord('issue');
}
Your template would make use of the input helpers like so:
{{input value=model.name}} and your action would just get the currentModel and call .save().

What array name does Ember Data expect for sub directory models?

I recently started learning Ember and using Ember-CLI so I'm not quite well educated about Ember Data and what array names it expects for relationships that are in sub directories in my app.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
serverHistory: DS.hasMany("history/server", { async: true })
});
// models/history/server.js
import DS from 'ember-data';
export default DS.Model.extend({
server: DS.belongsTo("server", { async: true })
});
I've tried returning these names from my API
server_historys_ids
server_histories_ids
history_server_ids
history_servers_ids
But I don't see an XHR request for Server history in my application. The servers itself are fetched fine.
Update
I changed my relationship name and the API is returning history ids but I'm still not getting an history json request even though I'm trying to each in the template. The game relationship data is accessible in the template and a request is successfully made.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
// attr's here.. not relevant
// Relationships
game: DS.belongsTo("game", { async: true }), // works
serverHistories: DS.hasMany("history/server", { async: true }) // doesn't make a request like game does.
});
I also have an adapter/history/server.js but it's only telling what namespace to use - "api".
Update 2
I think the problem may be in the way I'm calling the data to the model.
// routes/server/view/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var parentModel = this.modelFor("server.view");
return this.store.query("server", { server_address: parentModel.server_address });
// return this.store.find("server", 1);
}
});
How come when I use find with an id it updates the template data and when I use query with parameters it doesn't?
Update 3
So I got my find and query problem sorted out, here's the way I got it to work: https://stackoverflow.com/a/31831667/1814027
The relationship problem still persists. I see no serverHistory data in my Ember toolbar nor a request being made to the API for it.
I beleive serverHistory is anti-conventional name for hasMany and serverHistories should be instead.
export default DS.Model.extend({
serverHistories: DS.hasMany("history/server", { async: true })
});
Then in case of ActiveModelAdapter expected server payload is:
{"server": {"id": 1, "server_history_ids": [1,2,3]}}
It doesn't depend on the fact that serverHistory is namespaced model, it depends on relation name only.
For example for model:
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
bars: DS.hasMany("history/server", { async: true })
});
expected payload is:
{"server": {"id": 1, "bar_ids": [1,2,3]}}
Update
Working ember-cli example: https://github.com/artych/so_ember_data_subdir
Artych's answer helped me on the right path but Ember didn't want to recognise server_history_ids so I just renamed the hasMany relation to histories and returned histories: [] from my API. Now it works.. don't know why but it works.

Nested objects of same type ember data

I am trying to model the scenario below.
import DS from 'ember-data';
Type2 = DS.Model.extend({
value1: DS.attr('boolean'),
value2: DS.attr('boolean'),
value3: DS.attr('boolean')
});
Type1 = DS.Model.extend({
field1: DS.belongsTo('Type2'),
field2: DS.belongsTo('Type2')
});
In the model above it seems like the the parent -Type1 - does not get updated when I change a field in any of child - Type2 - objects.
What structure will the Json that is returned from the server have to be?
The documentation needs to be more descriptive because relationships are very common. Otherwise, the framework is nice. Good job.
Thanks,
Try calling save on parent after saving the child.
type2.save().then(function(){
type1.save();
});

Link to a nested emberData object in emberJS

I'm using emberData and I have the following model
App.Product = DS.Model.extend({
page_title: DS.attr('string'),
image: DS.attr('string'),
shop: DS.belongsTo('App.Shop', {embedded: true}),
style: (function() {
return "background-image:url('" + this.get("image") + "')";
})
});
The JSON data looks like this:
{
id: 1,
image: 'imageUrl',
shop: {
id: 2,
name: 'shopName'
}
}
In my template I want to link to the a page to display the shop
<img {{bindAttr src="image"}}>
{{#linkTo "shop" shop}}Store{{/linkTo}}
Unfortunately it links to http://localhost:3000/#/shop/undefined
It doesn't make sense to embed the model that it belongsTo. The breaking changes document states that you embed objects in a parent object:
From BREAKING_CHANGES.md
Embedded in Parent
An adapter can save one-to-many relationships by embedding IDs (or
records) in the parent object. In this case, the relationship is not
considered acknowledged until both the old parent and new parent have
acknowledged the change.
In this case, the adapter should keep track of the old parent and new
parent, and acknowledge the relationship change once both have
acknowledged. If one of the two sides does not exist (e.g. the new
parent does not exist because of nulling out the belongs-to
relationship), the adapter should acknowledge the relationship once
the other side has acknowledged.
Your fixture must be:
App.Product.FIXTURES = [{
id: 1,
image: "imageUrl",
shop_id: 2
}];