How to sideload ember hasMany and belongsTo Relationship? - ember.js

I have a Person Model as follows
App.Person= DS.Model.extend({
id: DS.attr('string'),
name: DS.attr('string'),
visits: DS.hasMany('App.Visit'),
events: DS.hasMany('App.Event') ,
allergies: DS.hasMany('App.Allergies'),
get_allergies : function(){
return this.get('allergies').getEach('allergy_name').reduce(function(accum, item) {
return (accum.length > 0) ? (accum +', '+ item) : (item);
}, '');
}.property('allergies.#each.allergy_name')
});
App.Visit = DS.Model.extend({
visit_id: DS.attr('string'),
date: DS.attr('date'),
admission: DS.belongsTo('App.Admission')
});
App.Admission = DS.Model.extend({
loc: DS.attr('string'),
admission_date: DS.attr('date'),
care_team: DS.belongsTo('App.CareTeam')
});
As you can see Person hasMany "allergies", and along with person, allergies is also getting loaded for me because in the UI I am calling the get_allergies method while other hasMany relationships like "visits" and "events" are not getting loaded.
In UI {{person.get_allergies}}
I tried to sideload the relationships "visits" and "events"(using example on net), but that is not working? Can someone tell what is the proper way of sideloading ember-data because I couldnt find any proper documention with example on net except for few questions on stackoverflow itself?

According to the documentation, you should just add additional Visit and Event data in the response from the server.
{
"person": {
"id": 1,
...
"event_ids": [5, 6, 7]
},
"events": [{
"id": 5,
...
},
{
"id": 6,
...
},
{
"id": 7,
...
}]
}
The main point here is that Events data should be outside of Person data.
Do you use the standard REST adapter? Could you please add a sample json returned by the server?

To sideload data in my app, I configure ember-data to know about the kinds of things I'll be sideloading. E.g. to sideload events and visits, do this:
DS.RESTAdapter.configure('App.Event', {
sideloadsAs: 'events'
});
DS.RESTAdapter.configure('App.Visit', {
sideloadsAs: 'visits'
});
App.Store = DS.Store.extend({
revision: 11
});
and then in your json can look like this:
{
"person": {
"id": 1,
"event_ids": [5, 6, 7],
"visit_ids": [1, 2, 3]
},
"events": [
{ "id": 5 },
{ "id": 6 },
{ "id": 7 }
],
"visits": [
{ "id": 1 },
{ "id": 2 },
{ "id": 3 }
]
}

I had the same problem but set up a bit different. visits were unaware of their person (i.e. couldn't do visit.get('person.name')). I had to add a serializer for visit:
export default AppSerializer.extend({
attrs: {
person: 'personId'
},
});
My payload looks like this:
{
person: {
id: 1,
name: 'John',
visitIds: [1, 2, 3]
},
visits: [
{ id: 1,
personId: 1
},
{ id: 2,
personId: 1
},
{ id: 3,
personId: 1
}
]
}
person model:
export default DS.Model.extend({
name: DS.attr('string'),
visits: DS.hasMany('visit')
});
visit model:
export default DS.Model.extend({
person: DS.belongsTo('person')
});

Related

How to display attributes of belongsTo object in Ember.js template

In my Ember app, a survey belongsTo a user; a user hasMany surveys. In my template, I would like to display a list of surveys, and the name of the user that created them. For now, I am pushing side-loaded data into the store via the application route, and it is showing up in the ember inspector->Data. The survey info is displaying correctly in the template, but the corresponding user's firstName will not appear. Help/guidance appreciated.
survey.js (model)
import DS from 'ember-data';
export default DS.Model.extend({
user: DS.belongsTo('user', {async: true}), //tried without async as well
title: DS.attr(),
post: DS.attr()
});
user.js (model)
import DS from 'ember-data';
export default DS.Model.extend({
surveys: DS.hasMany('survey', {async: true}),
firstName: DS.attr()
});
application.js (application route)
export default Ember.Route.extend({
model() {
this.store.push({
data: [{
id: 1,
type: 'survey',
attributes: {
title: 'My First Survey',
post: 'This is my Survey!'
},
relationships: {
user: 1
}
}, {
id: 2,
type: 'survey',
attributes: {
title: 'My Second Survey',
post: 'This is survey 2!'
},
relationships: {
user: 1
}
}, {
id: 1,
type: 'user',
attributes: {
firstName: 'Tyler'
},
relationships: {
surveys: [1, 2]
}
}]
});
}
});
surveys.js (route)
export default Ember.Route.extend({
model () {
return this.store.findAll('survey');
}
});
surveys.hbs (template)
<ul>
{{#each model as |survey|}}
<li>
<strong>{{survey.title}}</strong> //This works
<br>
{{survey.post}} //This works
<br>
Author: {{survey.user.firstName}} //This does not work
</li>
{{/each}}
</ul>
SOLUTION - updated application.js
export default Ember.Route.extend({
model() {
this.store.push({
"data": [ //Added double quotes throughout to conform to documentation
{
"id": "1",
"type": "survey",
"attributes": {
"title": "My First Survey",
"post": "This is my Survey!"
},
"relationships": {
"user": {
"data": {
"id": "1",
"type": "user"
}
}
}
}, {
"id": "2",
"type": "survey",
"attributes": {
"title": "My Second Survey",
"post": "This is survey 2!"
},
"relationships": {
"user": {
"data": {
"id": "1",
"type": "user"
}
}
}
}
],
"included": [
{
"id": "1",
"type": "user",
"attributes": {
"firstName": "Tyler"
} //no need to include user's relationships here
}
]
});
}
});
Payload relationship part is not correct. Should be:
relationships: {
user: {
data: {
id: 1,
type: 'user'
}
}
}
Also I think "user" payload should be in "included" section.
JSONAPISerializer api

ember JS returns only the last value from tastypie

I'm trying to get a list of items served from Django and Tastypie and display them using Ember.js. I've encountered the following problem:
EmberJS stores only the last value from JSON - so /api/post serves a few entries, but EmberJS stores and then displays only the last one(I also checked with the Ember extensions for Chrome). I'm not even sure where to look for a solution - is it Ember, Ember-data, Tastypie or all of them?
Here's the javascript code:
Blog.ApplicationAdapter = DS.DjangoTastypieAdapter.extend({
host: "http://127.0.0.1:8000",
namespace: "api/v1"
});
Blog.ApplicationSerializer = DS.DjangoTastypieSerializer.extend({});
/* Models */
Blog.Post = DS.Model.extend({
title: DS.attr('string'),
body: DS.attr('string'),
added: DS.attr('date'),
created: DS.attr('date')
});
/* Routes */
Blog.Router.map(function() {
this.route('post-list');
this.route('post', { path: 'post_id' });
});
Blog.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('post-list')
}
});
Blog.PostListRoute = Ember.Route.extend({
model: function() {
return this.store.find('post')
}
});
Here's the curl extract for reference:
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 2
},
"objects": [
{
"added": "2014-12-23T19:49:28.881759",
"body": "hsdkfkjahsdkshufy \r\n\r\nLorem ipsum",
"created": "2014-12-23T19:49:28.881759",
"id": 2,
"resource_uri": "\/api\/v1\/post\/2",
"slug": "jklshfkjasfhkjkhlf",
"title": "jkl;shfkjasfhkjkhlf"
},
{
"added": "2014-12-23T23:00:23.719586",
"body": "sdhkjasgaskg slkhs;fas\r\n\r\nhsdkjhsask sfhjask \r\nhakjhagkl\r\n\r\nhgaskjhagskj \r\nhgkjash",
"created": "2014-12-23T23:00:23.719586",
"id": 3,
"resource_uri": "\/api\/v1\/post\/3",
"slug": "asfhklasdnhjasdkghskl",
"title": "asfhklasdnhjasdkghskl"
}
]
}
I recreated the project using latest Django version (from 1.6 to 1.7). It seems that the issue is solved now, no changes in the code were made.

Displaying data for multi-level relationships with sideloaded json and Ember Data

I have an app that has many layers of relationships. I have a Tournament with n rounds, each round has n matchups, each matchup has n seats, each seat has 1 entry. Here's a sample of the json structure:
{
"tournament": {
"id": 1,
"title": "March Madness!!!",
"rounds": [1],
"active_round": 1
},
"rounds": [
{
"id": 1,
"tournament": 1,
"matchups": [1, 2]
},
{
"id": 2,
"tournament": 1,
"matchups": [3]
}
],
"matchups": [
{ "id": 1, "round": 1, "seats": [1, 2] },
{ "id": 2, "round": 1, "seats": [3, 4] },
{ "id": 3, "round": 2, "seats": [5, 6] }
],
"seats": [
{ "id": 1, "matchup": 1, "entry": 1 },
{ "id": 2, "matchup": 1, "entry": 2 },
{ "id": 3, "matchup": 2, "entry": 3 },
{ "id": 4, "matchup": 2, "entry": 4 },
{ "id": 5, "matchup": 3, "entry": "" },
{ "id": 6, "matchup": 3, "entry": "" }
],
"entries": [
{
"id": 1,
"seats": [1]
},
{
"id": 2,
"seats": [2]
},
{
"id": 3,
"seats": [3]
},
{
"id": 4,
"seats": [4]
}
]
}
I'm having trouble getting the content out. Here's my router.js:
App.Router.map( function() {
this.resource('tournament', { path: "/" });
});
App.TournamentRoute = Ember.Route.extend({
model: function() {
return new Ember.RSVP.Promise( function (resolve, reject) {
[..we just get the data return the json object above to setupController...]
});
},
setupController: function (controller, model) {
controller.set('model', model);
[i do a little data computation here prior to the renderTemplate function]
},
renderTemplate: function () {
var controller = this.controllerFor('tournament');
this.render('tournament');
}
});
My tournament.hbs template looks like this:
<h1>{{tournament.title}}</h1>
{{#each round in rounds}} Round id: {{round.id}} <br/>
{{#each matchup in round.matchups}} matchup id: {{matchup.id}}
<div class="matchup">
{{#each seat in matchup.seats}}
<div class="entry">
{{seat.entry.id}}
</div>
{{/each}}
</div>
{{/each}}
{{/each}}
And I'm getting the following on screen:
March Madness!!!
Round id: 1
matchup id:
matchup id:
Round id: 2
matchup id:
So, a little bit of it is working. I've done some work in the console and at the matchup level, the matchup object is actually the values "1" and "2", not matchups[0] and matchups[1], as expected, which is why there is no "id" attribute next to the matchup levels. I'm not sure how much "magic" there is in Ember data by using conventions, and can't find any examples that use this level of hierarchy. Thanks
UPDATE:
I'm including my models so as they are now, with the first responder's recommendations. I'm seeing the same results.
App.Tournament = DS.Model.extend({
title: DS.attr('string'),
active_round_index: DS.attr('number'),
rounds: DS.hasMany('App.Round', { embedded: 'always' })
});
App.Round = DS.Model.extend({
tournament: DS.belongsTo('App.Tournament'),
matchups: DS.hasMany('App.Matchup', { embedded: 'always' })
});
App.Matchup = DS.Model.extend({
round: DS.belongsTo('App.Round'),
seats: DS.hasMany('App.Seat', { embedded: 'always' })
});
App.Seat = DS.Model.extend({
matchup: DS.belongsTo('App.Matchup'),
entries: DS.hasMany('App.Entry', { embedded: 'always' })
});
App.Entry = DS.Model.extend({
title: DS.attr('string'),
seats: DS.hasMany('App.Seat')
});
** ANOTHER UPDATE **
So, as it turns out, the documented usage of the Ember.RSVP.Promise won't use all of the "magic" of Ember Data, which needs the RESTAdapter to do the fanciness. I plugged in the RESTAdapter and now things are working much better.
I can't see what your models look like, but one problem could be that you don't have the hasMany relationship definitions on your model marked as {embedded: 'always'}. This tells the ember serializer how to handle your related models when serializing and extracting.
example:
App.Round = DS.Model.extend({
tournament: DS.belongsTo('tournament'),
matchups: DS.hasMany('matchup', { embedded: 'always' })
});

Connecting to Express/Mongoose backend Ember-data rejects with "Object has no method 'eachTransformedAttribute'"

I'm using ember-data:
// Version: v1.0.0-beta.3-2-ga01195b
// Last commit: a01195b (2013-10-01 19:41:06 -0700)
var App = Ember.Application.create();
App.Router.map(function() {
this.resource("main");
});
Using a namespace:
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
The Ember model:
App.Article = DS.Model.extend({
title: DS.attr('string'),
desc: DS.attr('string')
});
The route looks like this:
App.MainRoute = Ember.Route.extend({
model: function() {
console.log(this.store.find('article')); // isRejected: true, reason: Object has no method 'eachTransformedAttribute'
this.store.find('article').then(function(results){console.log(results)}); //nothing
}
});
Here is the data:
{
"articles": [{
"_id": "5266057ee074693175000001",
"__v": 0,
"createdAt": "2013-10-22T04:56:30.631Z",
"desc": "testing, testing",
"title": "Basic",
"id": "5266057ee074693175000001"
}, {
"_id": "5266057ee074693175000002",
"__v": 0,
"createdAt": "2013-10-22T04:56:30.636Z",
"desc": "testing, testing",
"title": "Basic2",
"id": "5266057ee074693175000002"
}, {
"_id": "5266057ee074693175000003",
"__v": 0,
"createdAt": "2013-10-22T04:56:30.636Z",
"desc": "testing, testing",
"title": "Basic3",
"id": "5266057ee074693175000003"
}, {
"_id": "5266057ee074693175000004",
"__v": 0,
"createdAt": "2013-10-22T04:56:30.636Z",
"desc": "testing, testing",
"title": "Basic4",
"id": "5266057ee074693175000004"
}]
}
I am using ember-tools to manage the project build.
The issue is with ember-tools default build placing the Model definition after the Route.
UPDATED: This is because I manually created the Article model without using a generator. (I've since used the generator and the order is created correctly)
I've fixed it by manually updated the built: application.js from this:
App.MainRoute = Ember.Route.extend({
model: function() {
return this.store.find('document');
}
});
App.Article = DS.Model.extend({
title: DS.attr('string'),
file: DS.attr('string')
});
to this:
App.Article = DS.Model.extend({
title: DS.attr('string'),
file: DS.attr('string')
});
App.MainRoute = Ember.Route.extend({
model: function() {
return this.store.find('document');
}
});
I resolved this by inspecting a working app, and found that within the JSONSerializer applyTransforms() type was referencing a different type:
It should be the namespace model Class like this:

Trouble consuming json rest data in ember-data

I'm trying to parse a json dataset in my ember data models but this throws an error in het console:
Uncaught TypeError: Cannot call method '_create' of undefined
DS.Store.Ember.Object.extend.materializeRecord
DS.Store.Ember.Object.extend.findByClientId
What am I doing wrong here?
This is the data I receive from the server:
{
"newsitems": [
{
"date": "2013-02-10T15:00:00+01:00",
"id": "1",
"images": [
{
"id": "1",
"value": "image.JPG"
},
{
"id": "3",
"value": "anotherimage.jpg"
}
],
"slug": "some-slug",
"summary": "some summary",
"text": "some text",
"thumb": {
"id": "2",
"value": "someimage.JPG"
},
"title": "Some title",
"type": "1",
"videos": [
{
"id": "AEOpX8tmiUI",
"value": "AEOpX8tmiUI"
},
{
"id": "kxopViU98Xo",
"value": "kxopViU98Xo"
}
]
}
]
}
These are my models:
App.NewsitemThumb = DS.Model.extend({
value: DS.attr('string'),
newsitem: DS.belongsTo('App.Newsitem')
});
App.NewsitemImage = DS.Model.extend({
value: DS.attr('string'),
newsitem: DS.belongsTo('App.Newsitem')
});
App.NewsitemVideo = DS.Model.extend({
value: DS.attr('string'),
newsitem: DS.belongsTo('App.Newsitem')
});
App.Newsitem = DS.Model.extend({
slug: DS.attr('string'),
type: DS.attr('string'),
title: DS.attr('string'),
summary: DS.attr('string'),
text: DS.attr('string'),
date: DS.attr('date'),
thumb: DS.belongsTo('App.NewsitemThumb'),
images: DS.hasMany('App.NewsitemImage'),
videos: DS.hasMany('App.NewsitemVideo')
});
For the record, any suggestions for optimizing these models are welcome. It feels so weird to make 3 models for video, images and a thumb.
According to this issue, this error pops up when you don't specify an explicit mapping for a hasMany relationship in your adapter.
Try defining your store as
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.extend({
serializer: DS.RESTSerializer.extend({
init: function() {
this._super();
this.map("App.Newsitem", {
images: { embedded: "load" },
videos: { embedded: "load" }
});
}
})
})
});