Ember: Access sideloaded model relationship data in template - ember.js

I am trying to display a model's relationships in a template from sideloaded data, but there seems to be some problem. With Ember Inspector, I see that the relationships are correctly loaded from the data. However, the data is not displayed on the page.
Looking for solutions or suggestions on where to start debugging. Much appreciated.
Handlebars:
<dt>Categories</dt>
<dd>
<ul>
{{#each model.categories as |category| }}
<li>{{ category.name }}</li>
{{/each}}
</ul>
</dd>
The route:
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model(params) {
return this.store.findRecord('datasheet', params.id);
}
});
The models:
// app/models/datasheet.js
export default DS.Model.extend({
name: DS.attr('string'),
name_en: DS.attr('string'),
news: DS.attr('string'),
news_en: DS.attr('string'),
basic_information: DS.attr('string'),
basic_information_en: DS.attr('string'),
id_gradient_default: DS.attr('string'),
icon_name: DS.attr('string'),
icon_color: DS.attr('string'),
order: DS.attr('string'),
item_count: DS.attr('string'),
categories: DS.hasMany('category')
});
// app/models/category.js
export default DS.Model.extend({
name: DS.attr('string')
});
This is the JSON returned from the adapter method:
{
"data": {
"type": "datasheet",
"id": "21",
"attributes": {
"name": "Projekty",
"name_en": "Projects",
"news": "",
"news_en": "",
"basic_information": "",
"basic_information_en": "",
"id_gradient_default": "27",
"icon_name": "pin_flag",
"icon_color": "",
"order": "14"
},
"relationships": {
"categories": ["18", "19", "20", "51", "52"]
}
},
"included": [{
"type": "category",
"id": "18",
"attributes": {
"name": "Project"
}
}, {
"type": "category",
"id": "19",
"attributes": {
"name": "Activity"
}
}, {
"type": "category",
"id": "20",
"attributes": {
"name": "Project phase"
}
}, {
"type": "category",
"id": "51",
"attributes": {
"name": "Program"
}
}, {
"type": "category",
"id": "52",
"attributes": {
"name": "Milestone"
}
}]
}
Ember Inspector screenshot:

This is not correct JSONAPI:
"relationships": {
"categories": ["18", "19", "20", "51", "52"]
}
This is the correct JSONAPI equivalent:
"relationships": {
"categories": {
data: [{
id: '18',
type: 'category'
},{
id: '19',
type: 'category'
},{
id: '20',
type: 'category'
},{
id: '51',
type: 'category'
},{
id: '52',
type: 'category'
}]
}
}
So your data are loaded but not correctly linked. you can see this in the ember-inspector when you check the categories relationship.

Related

Load multiple model data in same api call emberjs?

So here is two models that i have defined in emberjs
match.js
import DS from 'ember-data';
export default DS.Model.extend({
team: DS.belongsTo('team', {async:true}),
opponent: DS.belongsTo('team', {async: true}),
type: DS.attr('string'),
squad: DS.attr('boolean')
});
and
team.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
logo: DS.attr('string')
});
I am already loading the match as a model. In the same api call i also want to load the model data for team. The api response that i have till now is
{
"meta":{
"type":"match"
},
"data":[
{
"id":1119536,
"type":"match",
"attributes":{
"id":1119536,
"team":{
"type":"team",
"id":1,
"attributes":{
"id":1,
"name":"England",
"logo":null
}
},
"opponent":{
"type":"team",
"id":3,
"attributes":{
"id":3,
"name":"Pakistan",
"logo":null
}
}
}
}
]
}
The match model data get loaded properly but i am having issues for the same with team data. The response is from network in browser and i already checked the model using ember plugin on browser that team data doesn't load. How can i use the same api call to load multiple models.
a few things to notice:
dont put the id in attributes
dont name an attribute type. Really dont! It's a reserved keyword.
relationships are not attributes and should be under relationships
use the included array to sideload data
ids must be strings
so for example this would be a valid payload:
{
"meta": {
"type": "match"
},
"data": [
{
"id": "1119536",
"type": "team",
"attributes": {
"match-type": "match"
},
"relationships": {
"team": {
"data": {
"type": "team",
"id": "1"
}
},
"opponent": {
"data": {
"type": "team",
"id": "3"
}
}
}
}
],
"included": [
{
"type": "team",
"id": "1",
"attributes": {
"name": "England",
"logo": null
}
},
{
"type": "team",
"id": "3",
"attributes": {
"name": "Pakistan",
"logo": null
}
}
]
}

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

How to use ember data with nested hasMany relationships?

I have some data models that look like this:
App.Order = DS.Model.extend({
name: DS.attr('string'),
legs: DS.hasMany('leg', {async: false})
});
App.Leg = DS.Model.extend({
name: DS.attr('string'),
order: DS.belongsTo('order'),
stops: DS.hasMany('stop', {async: false})
});
App.Stop = DS.Model.extend({
name: DS.attr('string'),
leg: DS.belongsTo('leg'),
});
The returned JSON looks like this:
{
"orders": [
{
"id": 1,
"name": "Order 1",
"legs": []
},
{
"id": 2,
"name": "Order 2",
"legs": [1]
},
{
"id": 3,
"name": "Order 3",
"legs": [2,3]
}
],
"legs": [
{
"id": 1,
"name": "Leg 1",
"stops": [1,2]
},
{
"id": 2,
"name": "Leg 2",
"stops": [2,3]
},
{
"id": 3,
"name": "Leg 3",
"stops": [1,3]
}
],
"stops": [
{
"id": 2,
"name": "Stop 2"
},
{
"id": 1,
"name": "Stop 1"
},
{
"id": 3,
"name": "Stop 3"
}
]
}
When I was using async:true, ember made http requests for the related legs (legs?ids[]=1 and legs?ids[]=2&ids[]=3) but would never make a request for the related stops.
So I switched to sideloading as shown above, but the stops are still NOT available from the related legs.
Can you help me understand how the loading of Models with nested hasMany relationships SHOULD be done? Or where I've gone wrong? I'm open to using either async: true or async: false, as long as I can get the nested data loaded in to the model.

Ember.js: Accessing related model properties

I can't figure out how to output properties from related models into views. Models are defined like this:
Club Model
App.Club = DS.Model.extend({
name: DS.attr('string'),
slug: DS.attr('string'),
... // Rest of omitted attributes
province: DS.belongsTo('App.Province'),
city: DS.belongsTo('App.City'),
servicetable: DS.belongsTo('App.Servicetable'),
timetable: DS.belongsTo('App.Timetable'),
pricetable: DS.belongsTo('App.Pricetable')
});
Servicetable Model, which belongs to Club
App.Servicetable = DS.Model.extend({
labels: {
parking: 'Parking',
bikeParking: 'Aparcamiento para bicicletas',
shower: 'Duchas',
materialRenting: 'Alquiler de Material',
restaurantCoffeeshop: 'Restaurante/Cafetería',
shop: 'Tienda',
playschool: 'Guardería',
locker: 'Taquillas',
handicapped: 'Acceso minusválidos',
sauna: 'Sauna',
wifi: 'WiFi'
},
parking: DS.attr('boolean'),
bikeParking: DS.attr('boolean'),
shower: DS.attr('boolean'),
materialRenting: DS.attr('boolean'),
restaurantCoffeeshop: DS.attr('boolean'),
shop: DS.attr('boolean'),
playschool: DS.attr('boolean'),
locker: DS.attr('boolean'),
handicapped: DS.attr('boolean'),
sauna: DS.attr('boolean'),
wifi: DS.attr('boolean'),
club: DS.belongsTo('App.Club')
});
Now, the output for api/clubs/2 is the following. Note that servicetable is being sideloaded to reduce number of requests:
{
"club": {
"id": "2",
"name": "Club de P\u00e1del y Tenis Fuencarral",
"slug": "club-de-padel-y-tenis-fuencarral",
... // Rest of omitted attributes
},
"servicetable": {
"id": "2",
"club_id": "2",
"parking": "1",
"bike_parking": "0",
"shower": "1",
"material_renting": "1",
"restaurant_coffeeshop": "1",
"shop": "1",
"playschool": "0",
"locker": "0",
"handicapped": "0",
"sauna": "0",
"wifi": "0"
}
}
Finally, route and template:
App.ClubGeneralInfoRoute = Em.Route.extend({
setupController: function(controller) {
controller.set('content', App.Club.find(App.clubId));
}
});
<script type="text/x-handlebars" data-template-name="club/general-info">
<h1>Club Info</h1>
<label>Parking</label>
{{view Ember.Checkbox checkedBinding="content.servicetable.parking"}}
</script>
So when I load the page, I can see the template rendering as the <h1> is appearing, but the properties of Servicetable not.
Note that if I try to render direct Club attributes like name or slug in this template, they are working too.
Any suggestions?
If you're sideloading, you need to configure your adapter to do so.
DS.RESTAdapter.configure('App.Servicetable', {
sideloadAs: 'servicetables'
});
Next, you need to include the servicetable id in the club's json:
"club": {
"id": "2",
"servicetable_id": 2,
... // Rest of attributes
}
When you side load records, pass them as an array, and set the key in plural form:
"servicetables": [{
"id": "2",
... // Rest of attributes
}]
Finally, the complete json response should look like this:
{
"club": {
"id": "2",
"servicetable_id": 2,
"name": "Club de P\u00e1del y Tenis Fuencarral",
"slug": "club-de-padel-y-tenis-fuencarral",
... // Rest of attributes
},
"servicetables": [{
"id": "2",
"club_id": "2",
"parking": "1",
... // Rest of attributes
}]
}

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" }
});
}
})
})
});