Rendering nested objects in Ember.js - ember.js

New to Ember and I cannot figure out how to render nested options either via associated ember-data models or simply as json objects.
JSON looks like this:
contact: {
first_name,
last_name,
emails: [{email1...}, {email2...}]
}
Looking for the easiest solution, I tried this in handlebars template:
{{#each emails}}
...
{{/each}}
Trying a more advanced solution, which will eventually be needed I created the following ember-data models:
App.Contact = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
company: DS.attr('string'),
emails: DS.hasMany('Rainmaker.Email')
});
App.Email = DS.Model.extend({
contact_id: DS.attr('number'),
email_address: DS.attr('string'),
emails: DS.belongsTo('Rainmaker.Contact')
});
Which seems to be working somewhat (App.Email:ember517 renders on the screen) but it looks like it's still trying to ping the server to get the email addresses and I've already got them lazy loaded.
I have no idea where to go next and greatly appreciate any guidance or links to documentation that I've overlooked. Thanks.

On 28. dec the Ember team has included support for embedded records. If you look at https://github.com/emberjs/data at about 1/3 of the page, it gives you some directions on this.
In your case, simply altering emails: DS.hasMany('Rainmaker.Email') to emails: DS.hasMany('Rainmaker.Email',{embedded:true}) should do the trick according to the docs.

Related

Ember data show length of a hasMany relationship in a template without downloading all the objects of that relationship

I have a product model that hasMany prices. I have a page that displays the products name, code and then the number of prices it has i.e:
<tbody>
{{#each model as |product id|}}
<tr>
<td>{{#link-to "product" product}}{{product.name}}{{/link-to}}</td>
<td>{{product.code}}</td>
<td>{{product.prices.length}}</td>
</tr>
{{/each}}
</tbody>
The issue I have is that by using product.price.length Ember data is making thousands of requests to get the prices by id. I don't need any information about the actual price on this page. How can I use the length property here without Ember data downloading all the prices?
models/product.js:
export default DS.Model.extend({
code: DS.attr('string'),
name: DS.attr('string'),
prices: DS.hasMany('price', {async: true})
});
models/price.js
export default DS.Model.extend({
product: DS.belongsTo('product', {async: true}),
value: DS.attr('number'),
minUnits: DS.attr('number'),
maxUnits: DS.attr('number')
});
Having chatted in the ember slack room I have two current solutions, one backend, one frontend.
Frontend
As of Ember data 2.5 there is a new 'ds-references' feature as detailed in the release post http://emberjs.com/blog/2016/05/03/ember-data-2-5-released.html.
With this the solution here would be to add a computed with something like:
priceCount: Ember.computed('prices.[]', function() {
if (this.hasMany('prices').value() === null) {
return 0;
}
return this.hasMany('prices').ids().length;
}
It's been reported in the comments that the above may trigger backend requests. As an alternative, you could add a helper function totalHasMany with code
return param[0].hasMany(param[1]).ids().length
and use it in a template with (totalHasMany product 'prices'), I've used this in an app with success.
Backend
Look at using meta data to return the price total. So in a Json Api settings something like
"meta": {
"prices-total": 123
}
See http://jsonapi.org/format/#document-meta for further info.
https://guides.emberjs.com/v2.1.0/models/handling-metadata/ may also be useful to those on standard json.
Thanks to kitler and locks for the above suggestions in slack.

How to sort Ember Data model output?

I've searched and searched, but all the examples seem either outdated, aren't in the file structure enforced by Ember CLI, etc.
Anyway, say I have a model in app/models/employee.js:
export default DS.Model.extend({
firstname: DS.attr('string'),
lastname: DS.attr('string'),
});
And a route in app/routes/employees.js:
export default Ember.Route.extend({
model: function() { return this.store.findAll('employee'); },
});
And a template in app/routes/employees.hbs:
{{#each model as |employee|}}
<tr><td>{{employee.firstname}}</td><td>{{employee.lastname}}</td></tr>
{{/each}}
What do I need to add to sort that table by firstname, for example?
I gather I'm supposed to use Ember.computed.sort(), something like:
sortedEmployee: Ember.computed.sort('employees', ['firstname'])
And then do {{#each sortedEmployee as ...}} in the template, but I'm apparently not defining sortedEmployee in the right place.
app/controllers/employees.js
export default Ember.Controller.extend({
sortProperties: ['firstname:asc'],
sortedEmployees: Ember.computed.sort('employees', 'sortProperties')
});
app/routes/employees.hbs:
{{#each sortedEmployees as |employee|}}
{{employee.firstname}}
{{/each}}
Example in JSbin: http://emberjs.jsbin.com/regowa/7/edit?html,css,js,output
You are on the right track, try the following:
sortProperties: ['firstname:asc'], // or just 'firstname', or 'firstname:desc'
sortedEmployee: Ember.computed.sort('employees', 'sortProperties')
I think it's weird to have to define an extra property on the component/controller, but that way it works.

Ember app won't load data from has_many relationships using ActiveModelAdapter

This app works fine when the store is FixtureAdapter, but will not load has_many relationships when the store is ActiveModelAdapter.
The Route is like this:
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.find('post', 1 )
}
});
The models are like this:
App.Post = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
comments: DS.hasMany('comments', {embedded: 'always'})
});
App.Comment = DS.Model.extend({
text: DS.attr('string'),
post: DS.belongsTo('post')
});
The handlebars template calls for:
{{name}}
{{#each comments}}
{{text}}
{{/each}}
The name appears, the comments do not.
In the ember developer console, the comments are not getting loaded at all.
The server seems to be serving the serialized content correctly (as far as I can tell). It looks like this:
{"post":{"id":1,"name":"Title","description":"Lorem Ipsum","comments":[{"id":1, "text": "commentary here"}]}}
Does anyone know why this isn't working and how I could fix it? I've been struggling with this for hours now and cannot figure out why it's not working. Thanks very much for any insight you can provide.
I'm using this as part of a Rails project, and these are the relevant gems:
gem 'ember-rails'
gem 'ember-source', '1.3.0'
gem 'ember-data-source', '~> 1.0.0.beta.6'
gem "active_model_serializers"
You need to specify a custom adapter for each over-riden model and mixin the Embedded Records Mixin:
App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
comments: {embedded: 'always'}
}
});
Please see the comments in the Ember-Data source code for more info.

Can I do hasMany relationships in Ember with Model-specific data for each?

Okay, so, without giving way too much detail, I'm trying to build an RPG character sheet as my first project with ember.js (though, I've already done the TodoMVC tutorial, as well as Tom Dale's video guide, and the Peepcode video. It is, however, likely that I've already lost/forgotten bits and pieces, so forgive me if this question is covered there).
Basically, just tracking character stats and whatnot. Most of the stats are pretty straightforward, as they're just strings/numbers. What's tripping me up a bit is when I get to things like character inventory, skills, etc.
For example, in trying to set up my data model, I've got this for the simple attributes:
App.Character = DS.Model.extend({
species: DS.attr('string'),
career: DS.attr('string'),
gender: DS.attr('string'),
age: DS.attr('number'),
height: DS.attr('number'),
hair: DS.attr('string'),
notableFeatures: DS.attr('string'),
(etc., etc.)
});
Now, for other attributes, like, say, an astrogation skill, I've got both the skill name and the rank the character has in that skill.
One option would be to just get kind of lazy and set up each skill as follows:
skillAstrogation: DS.attr('number')
I don't feel like that's quite in the "spirit" of things, though, and it would make it harder for me to add/remove skills later on if necessary. Also, I'd like, at some point, for the user to be able to add their own skills.
So, after asking on reddit, and doing some more research, it looks like I should be using a many-to-many relationship. That is, set up a separate model for "skills", and give each a name, rank, etc. (there's another field or two I'll want, as well).
Something like this:
App.Character = DS.Model.extend({
...
skills: DS.hasMany("App.Skill")
});
App.Skill = DS.Model.extend({
title: DS.attr('string'),
rank: DS.attr('number'),
characters: DS.hasMany("App.Character")
});
What I'm not entirely clear on, though, is how I then set the rank for each character per-skill, if that makes sense.
Maybe "rank" shouldn't be in App.Skill? Should it be in the Character model, instead?
I hope I'm being clear, here. :)
Thanks for any input!
As I understand correctly what you want is not actually many to many but many to one. Each skill rank only corresponds with one Character right? Now you might feel that this is adding a lot of duplicate content since the title would be copied each time, in this case you would actually like 3 models.
Something like this:
App.Character = DS.Model.extend({
...
skills: DS.hasMany("App.Linkage")
});
App.Linkage = DS.Model.extend({
rank: DS.attr('number'),
character: DS.belongsTo("App.Character")
skill: DS.belongsTo("App.Skill")
});
App.Skill = DS.Model.extend({
title: DS.attr('string'),
...
});
To now retrieve the data you could do the following:
route:
model: function () {
return App.Character.find();
}
template:
<ul>
{{#each character in controller}}
<li>{{character.name}} -
{{#each item in character.skills }}
{{item.skill.skill}}: {{item.rank}},
{{/each}}
</li>
{{/each}}
</ul>

How to load child object with parent in ember

I am using Ember RC2 and Ember-Data 12 have the following relationship (simplified):
App.GlobalData = DS.Model.extend({
lat: DS.attr('number'),
lon: DS.attr('number'),
});
App.System = DS.Model.extend({
title: DS.attr('string'),
data: DS.belongsTo('App.GlobalData'),
});
In my (System) view I now want to access the child's data like this: {{ data.lat }}
It seems like Ember (currently I am using the FixtureAdapter, but I would also like to make this work with the RESTadapter in the future) does not automatically load the child element data. While {{ data.id }} returned the value of the id (as specified in the App.GlobalData.FIXTURES), {{ data.lat }} returned undefined.
I somewhat got around this issue by creating an array controller:
App.GlobalDatasController = Ember.ArrayController.extend({});
App.globalDatasController = App.GlobalDatasController.create({});
and preloading all GlobalData in my ApplicationRoute
App.ApplicationRoute = Ember.Route.extend({
setupController: function() {
App.globalDatasController.set('content', App.GlobalData.find());
}
});
However, this does not seem like a good solution, because it requires me to load all GlobalData, even though I may only need one.
I am sure there is a best practice on how to handle this, however, despite my best research efforts I have not been able to find it.
To summarize my question:
How and where do I tell ember to load child data with the parent (without sideloading it)?
If sideloading is the only option, how would I implement that in the FixtureAdapter?
Thanks!
I found the problem: In my FIXTURES, I specified IDs as integers instead of strings. Not sure why that would make a difference, but once I changed that, it worked.