Consider the following simple models...
//models/invoice.js
export default DS.Model.extend({
//created_by: DS.attr('number'), //points to a user.id
created_by: DS.belongsTo('user', {async: true, inverse: created_by}),
//approved_by: DS.attr('number'), //also points to a user.id
approved_by: DS.belongsTo('user', {async: true, inverse: approved_by}),
});
//models/user.js
export default DS.Model.extend({
name: DS.attr('string')
created_by: DS.belongsTo('invoice', {async: true, inverse: 'created_by'}),
approved_by: DS.belongsTo('invoice', {async: true, inverse: 'approved_by'})
});
And relevant JSON...
{
"invoices":
[
{
"id": "2",
"created_by": "103",
"approved_by": "109",
.....
I am trying to get the Names values for each of the ID values in any given Invoice record.
# | Created | Approved
--------------------------
2 | Jim | Bobby
3 | Sue | Betty
I can get the approved_by relationship to populate data in my template....
{{item.approved_by.id}} {{item.approved_by.name}}
but not created_by...
{{item.created_by.id}} {{item.created_by.name}}
I can see some data being rendered in the template then it goes away.
I can see the correct requests sent off to the API...
http://localhost:4200/RESTAPI/v1/users/110
http://localhost:4200/RESTAPI/v1/users/200
My question: What am I doing wrong?
This is where Ember relationships come into play. In your particular example, you need to tell Ember that your Invoice model belongs to your User model. And, vice versa, your User model can have many invoices related to it.
The actual syntax for this can be found here:
http://emberjs.com/guides/models/defining-models/#toc_one-to-many
Once you wire that up properly, you can do something like:
invoice.get('user.name')
Related
I am having issues loading a belongsTo relationship - no errors get displayed, and no requests sent. The UI just remains blank. Given the following models:
project.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr(),
items: DS.hasMany('line-item', {async: true}),
customer: DS.belongsTo('customer', {async: false})
});
customer.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr(),
email: DS.attr(),
projects: DS.hasMany('project', {async: true})
});
The relationship between project and customer exists. When accessing the projects endpoint, the project gets correctly returned:
{
"data":{
"type":"projects",
"id":"3861b834-e270-4296-b7be-9aca55676874",
"attributes":{
"created":"2016-04-27T22:36:01.061349Z",
"modified":"2016-04-27T22:36:01.061477Z",
"name":"Sample name",
},
"relationships":{
"customer":{
"data":{
"type":"customers",
"id":"9242bd41-6bb0-41ed-b5f3-21df26486d9e"
}
},
"items":{
"meta":{
"count":0
},
"data":[
]
}
}
}
}
However when trying to access the customer, nothing happens (with nothing I mean: no console output, no request to the customers endpoint etc. The UI just fails to load):
this.get('project').get('customer');
Accessing other attributes works (including the project's items).
Any idea as to where I am going wrong?
In your project model you defined customer as async: false, which means it should be provided when loading the projects from your server. From the json output you provided the customer data is missing.
So either include the customer record when returning the json from your server or make customer async: true, so it will get loaded when calling project.get('customer')
I have problem defining the relationship between my models in order to get cascading property.
I would like to MapLineString to be deleted when Trail is deleted or Draw is deleted. But I DO NOT want Trail to be deleted when MapDraw or MapLineString gets deleted.
Relationship between the models is :
Trail can have one Trailer, one Team and one mapDraw
MapDraw can have many MapLineString
MapLineString can belongs to Trail AND/OR MapDraw
Trail = DS.Model.extend({
Trailer: DS.belongsTo('mapLinestring', {async: true, inverse: 'trail'}),
Team: DS.belongsTo('mapLinestring', {async: true, inverse: 'trail'}),
mapDraw: DS.belongsTo('mapDraw', {async: true}),
});
MapDraw = DS.Model.extend({
lineStrings: DS.hasMany('mapLinestring', {async: true}),
trail: DS.belongsTo('mtgTrail')
});
MapLineString = DS.Model.extend({
trail: DS.belongsTo('mtgTrail'),
mapDraw: DS.belongsTo('mapDraw'),
});
Assertion Failed: You defined the 'trail' relationship on
mantrailling#model:map-linestring:, but you defined the inverse
relationships of type mantrailling#model:mtg-trail: multiple times.
Look at
http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses
for how to explicitly specify inverses
Looking at what youve written at the beginning of the post it sounds like
A should be:
export default DS.Model.extend({
bees: DS.hasMany('B', {async: true, inverse: 'abees'}),
cees: DS.hasMany('C', {async: true}), //doesnt need an inverse as you are getting all the ones that belong to A
});
B should be:
export default DS.Model.extend({
cees: DS.hasMany('C', {async: true, inverse: 'bcees'}),
abees: DS.belongsTo('A')
});
and then in C you have two model properties called the same thing
export default DS.Model.extend({
acees: DS.belongsTo('A'),
bcess: DS.belongsTo('B')
});
Your naming conventions also make it quite confusing. Why not just name the attrs of the models something thats relevant to what they represent?
Ok, I found out what the problem was.
I have been using Localstorage adapter, which does not work with {async: true}.
The records were not persisted on the parent side.
Say I have two Ember Data models: competition and participant
My competition model stores the following data: date, first, second and third. The first, second and third properties will all store IDs of the unique competitors who came in first, second and third place.
My participant model stores the following data: name, surname and competition
Participants only compete in one competition, so the participant model doesn't need a hasMany relationship on competition.
How do I model this in Ember Data?
Here's what I thought might work:
// app/models/competition.js
export default DS.Model.extend({
rev: DS.attr('string'),
date: DS.attr('date', {defaultValue: function() { return new Date(); }}),
first: DS.belongsTo('participant'),
second: DS.belongsTo('participant'),
third: DS.belongsTo('participant')
});
// app/models/participant.js
export default DS.Model.extend({
rev: DS.attr('string'),
name: DS.attr('string'),
surname: DS.attr('string'),
competition: DS.belongsTo('competition')
});
The problem is that the competition key on participant.js requires an explicit inverse, and there's no way of knowing which of the three keys on competition it may relate to.
How should I be specifying my inverses? Do both the competition and participant models require explicit inverses? If so, what should they be? Is this kind of data structure even possible in Ember Data? Should I use a different approach altogether?
Please weigh in. I've turned this problem around in my head for hours, and none of my workarounds have succeeded.
I haven't used ember-data polymorphic association myself, but I believe a polymorphic belongsTo association might suit your needs. My answer is adapted from this blog post
. Also check this two tutorials: http://www.toptal.com/emberjs/a-thorough-guide-to-ember-data and http://blog.unspace.ca/post/107228551852/luke-galea-on-ember-data-polymorphic-associations
//app/models/competition.js
export default DS.Model.extend({
rev: DS.attr('string'),
date: DS.attr('date', {defaultValue: function() { return new Date(); }}),
participant: DS.belongsTo("particpant", {polymorphic: true, async: true})
});
// app/models/participant.js
export default DS.Model.extend({
rev: DS.attr('string'),
name: DS.attr('string'),
surname: DS.attr('string'),
competition: DS.belongsTo('competition')
});
// app/models/first.js
import Participant from '../participant';
export default Participant.extend({
});
// app/models/second.js
import Participant from '../participant';
export default Participant.extend({
});
I am trying to serialize a user object and pass it to an ember client. My app uses the RESTserializer.
A user object has_one address object. There is no separate endpoint for just the address so I can't just provide a foreign key id and sideload it.
The JSON received just includes address as an object and looks something like this:
{"user":
{
"id":"5",
"name":"Andrew",
"address":
{
"id":"3",
"addressable_id":"5",
"street":"1",
"country_code":"US"
}
}
On the ember side I have a user model
App.User = DS.Model.extend({
name: DS.attr('string'),
address: DS.belongsTo('address'),
//hasOne via belongsTo as suggested by another SO post:
//http://stackoverflow.com/questions/14686253/how-to-have-hasone-relation-with-embedded-always-relation
});
and an address model
App.Address = DS.Model.extend({
addressable_id: DS.attr('string'),
street: DS.attr('string'),
country_code: DS.attr('string'),
user: DS.belongsTo('user')
});
Currently running this code throw an error in the console:
TypeError: Cannot read property 'typeKey' of undefined
which can be fixed by removing the
address: DS.belongsTo('address'),
line in the user model but then the relationship doesn't load properly.
So what am I doing wrong configuring this? I am having a hell of a time finding up to date documentation on this.
You need to use the DS.EmbeddedRecordsMixin on a per-type serializer.
In your case, you would need to do the following :
App.UserSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
address: {embedded: 'always'}
}
});
as explained in this excellent answer.
I have a system where I want users to add flags (hashtags) to items. Here's my models:
Social.Item = DS.Model.extend({
source: DS.belongsTo ('source'),
url: DS.attr (),
post_timestamp: DS.attr(),
summary: DS.attr(),
item_flags: DS.hasMany('item_flag', {async: true})
});
Social.Flag = DS.Model.extend({
kind: DS.attr(),
name: DS.attr(),
item_flags: DS.hasMany('item_flag', {async: true})
});
Social.ItemFlag = DS.Model.extend({
item: DS.belongsTo('item', {async: true}),
user: DS.belongsTo('user', {async: true}),
flag: DS.belongsTo('flag', {async: true}),
});
Here's the relevant handlebars code:
{{#each item in site.sorted_items itemController="item"}}
{{{item.summary}}}
{{#each item_flag in item.item_flags}}{{item_flag.flag}}*FF*{{/each}}
{{/each}}
The system outputs a few FF tags for each item - there's definitely item_flag elements in the database, and I can see the calls on my REST endpoint - the system is requesting the item_flags from the database. It just seems like the object on the other side of the belongsTo relationship isn't available here. I tried extending the ItemFlag model to contain this code:
flag_string: function() {
return this.get('flag').then(function (data) {return data.get('name')});
}.property('flag')
But that just throws a NPE - this.get('flag') on the ItemFlag model returns null. There seems to be some mention of an "embed: always" defined on the adapter, but (a) that's from pre- ember-data-beta days, and (b) it didn't help (also, my records aren't embedded).
What I want to do in the general case here is figure out how to look at a model's parent model, as defined by the relationships in the database.
Turns out, I fell afoul of the new ember-data serializer expectations. My item_flag serializer was responding with json that looked like {id: 1, flag_id:1, user_id:1} when what it should have looked like was {id: 1, flag:1, user:1}. This caused (apparently), the item_flag to get created without the proper parameters.