Ember: Rendering nested loops from multiples related models - ember.js

I'm not able to render the following nested loop:
<script type="text/x-handlebars" id="chapters">
<h4>Chapters</h4>
<ul>
{{#each chapter.book}}
<li>
{{book.name}}
{{#each}}
<li>
{{name}}
</li>
{{/each}}
</li>
{{/each}}
</ul>
</script>
What I'm trying to accomplish is to loop through all the books and for each book to loop through its chapters. The model return all the chapters. I suspect that my Handlebars template syntax is not correct.
The following question is helpful but it didn't solve my issue:
How to use multiple models with a single route in EmberJS / Ember Data?
The two models in questions are:
App.Book = DS.Model.extend({
name: DS.attr('string'),
icon: DS.attr('string'),
description: DS.attr('string'),
createdDate: DS.attr('date'),
chapters: DS.hasMany('chapter', { inverse: 'book', async: true}),
bookAssignments: DS.hasMany('bookAssignment', {async: true}),
user: DS.belongsTo('user', { inverse: 'books', async: true} ),
});
App.Chapter = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
unit: DS.attr('string'),
dashboard: DS.attr('boolean'),
createdDate: DS.attr('date'),
source: DS.attr('string'),
book: DS.belongsTo('book', { inverse: 'chapters', async: true}),
user: DS.belongsTo('user', { inverse: 'chapters', async: true} )
});
The route:
App.ChaptersRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('chapter');
},
});
Thanks in advance.

In your case you'd want to get all of the unique books, and iterate through them. With a chapters controller it'd be something like this:
App.ChaptersController = Ember.ArrayController.extend({
//content because they are promises
books: Ember.computed.mapBy('model', 'book.content'),
uniqueBooks: Ember.computed.uniq('books'),
});
Template (I added another UL)
<script type="text/x-handlebars" id="chapters">
<h4>Chapters</h4>
<ul>
{{#each book in uniqueBooks}}
<li>
<ul>
{{book.name}}
{{#each chapter in book.chapters}}
<li>
{{chapter.name}}
</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>
Example of the unique: http://emberjs.jsbin.com/vuhaga/3/edit?html,js,output

Related

Ember, There is no route named contact error

I'm a newbie to Ember. I used latest version 2.6.2. I was struggling with a route. When I go to contacts page I got an error like this Error: There is no route named contact.
My app/route.js
Router.map(function() {
this.route('contacts', function() {
this.route('show', { path: '/:contact_id' });
});
});
My app/routes/contacts/index.js
export default Ember.Route.extend({
model: function() {
return this.store.findAll('contact');
}
});
My app/templates/contacts/index.hbs
<ul>
{{#each model as |contact|}}
<li>
{{#link-to 'contact' contact}}
{{contact.lastName}},
{{contact.firstName}}
{{/link-to}}
</li>
{{else}}
<li>No contacts found.</li>
{{/each}}
</ul>
My app/models/contact.js
import DS from 'ember-data';
export default DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
email: DS.attr('string'),
title: DS.attr('string'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date')
});
Thanks for your help.
You will get that error if link-to is linking to a route that is not found in your routes.
You have two named routes in router.js 1. contacts 2. contacts.show
Change your app/templates/contacts/index.hbs file.
<ul>
{{#each model as |contact|}}
<li>
{{#link-to 'contacts.show' contact}}
{{contact.lastName}},
{{contact.firstName}}
{{/link-to}}
</li>
{{else}}
<li>No contacts found.</li>
{{/each}}
</ul>

How to pass computed properties from route to component

Taking the following as an example:
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each model as |item|}}
<li>{{item}}</li>
{{/each}}
{{magnus-component name=firstName}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="components/magnus-component">
<!-- Doesn't appear -->
{{name}}
</script>
...and
App = Ember.Application.create();
App.Router.map(function() {
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
},
name: "Magnus",
surname: "Womble",
fullName: Ember.computed('name', 'surname', function(){
return this.get('name') + ' '+this.get('surname');
})
});
Which can be found in the following jsbin:
http://emberjs.jsbin.com/higako/edit?html,js,output
How do you correctly pass the fullName property to the component template?
You should move your logic from IndexRoute to IndexController:
App.IndexController = Ember.Controller.extend({
name: "Magnus",
surname: "Womble",
fullName: Ember.computed('name', 'surname', function(){
return this.get('name') + ' '+this.get('surname');
})
});
And then, if you use:
{{magnus-component name=fullName}}
instead of (firstName isn't defined anywhere):
{{magnus-component name=firstName}}
It will work.
Working demo.

Handling Two hasMany Relationships

What would be the best way to have and object that has two hasMany relationships.
Models:
App.Gradebook = DS.Model.extend({
title: DS.attr('string'),
students: DS.hasMany('student', { async: true }),
assignments: DS.hasMany('assignment', { async: true })
});
App.Student = DS.Model.extend({
name: DS.attr('string'),
gradebook: DS.belongsTo('gradebook'),
grades: DS.hasMany('grade', { async: true })
});
App.Assignment = DS.Model.extend({
title: DS.attr('string'),
total: DS.attr('number'),
gradebook: DS.belongsTo('gradebook'),
grades: DS.hasMany('grade', { async: true })
});
App.Grade = DS.Model.extend({
score: DS.attr('number'),
student: DS.belongsTo('student'),
assignment: DS.belongsTo('assignment')
});
Currently, this is the way I have it setup:
Template:
<script type="text/x-handlebars" data-template-name="gradebooks">
<ul class="nav nav-tabs">
{{#each}}
<li {{bind-attr class="isActive:active"}}>
{{#link-to "gradebook" this}}
{{title}}
{{/link-to}}
</li>
{{/each}}
</ul>
<div class="content">
{{#if hasGradebooks}}
{{outlet}}
{{/if}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="gradebook">
<h2>{{title}}</h2>
<table>
<thead><tr>
<th>Students</th>
{{#each assignments}}
<th>{{title}} ({{total}})</th>
{{/each}}
</tr><thead>
<tbody>
{{#each student in students}}
<tr>
<td>{{student.name}}</td>
{{#each assignment in assignments}}
<td>{{student}}{{assignment}}{{getGrade student assignment}}</td>
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
</script>
Controllers:
App.GradebookController = Ember.ObjectController.extend({
getGrade: function(student, assignment) {
console.log(arguments);
}
});
Now, everything works fine and dandy except for the call to getGrade. The output of arguments shows that student and assignment are being passed as the literal string "student" and "assignment". The regular output of {{student}}{{assignment}} is there for debugging and outputs something like <App.Student:ember459:1><App.Assignment:ember453:1> so I know that they are objects.
How can I get the Student and Assignment object from the getGrade call?
To be honest, I'm not even sure how you're getting the getGrade method to be called. I don't think that's valid Hanldebars syntax. To do what you want, you're going to have to write a Handlebars helper as detailed in the Ember.js guide here.

Ember Data pluralizing url endpoints when using store.find

I got an error with
GET http://localhost:63342/people 404 (Not Found)
and
Assertion failed: Error while loading route:....
Why does this error http://localhost:63342/people 404 appears? I don't have this route.
here is my js code:
window.Models = Ember.Application.create();
Models.Router.map(function () {
//匹配路由后显示person_list模板
this.resource('persons', { path: '/' });
});
Models.PersonsRoute = Ember.Route.extend({
model:function(){
return this.store.find('person');
},
setupController:function(controller, model){
controller.set('content', model)
}
});
Models.Person = DS.Model.extend({
name: DS.attr('string'),
description:DS.attr('string')
});
Models.Person.FEATURES = [{
name:'Kratos Zhang',
description:'a c# coder in 7agree.'
}]
and template
<script type="text/x-handlebars" data-template-name="persons">
<ul>
{{#each item in persons}}
<li>
<h4>{{item.name}}</h4>
<p>{{item.description}}</p>
</li>
{{/each}}
</ul>
why isn't it working?
You can override pathForType in the adapter if that isn't your endpoint, Ember Data by default pluralizes endpoints
App.ApplicationAdapter = DS.RESTAdapter.extend({
pathForType: function(type) {
//return Ember.String.pluralize(type);
return type;
},
});
This isn't necessary, you can delete it
setupController:function(controller, model){
controller.set('content', model)
}
persons doesn't exist in the template
{{#each item in model}}
<li>
<h4>{{item.name}}</h4>
<p>{{item.description}}</p>
</li>
{{/each}}
or
{{#each item in controller}}
<li>
<h4>{{item.name}}</h4>
<p>{{item.description}}</p>
</li>
{{/each}}

How to manage 2 nested dynamic segments with ember?

EDIT: I found this very interesting question and answer here, but unfortunately it does not work with Ember 1.0. It would be great if someone could edit it for ember 1.0. The JsFiddle is here.
I want to articulate my app around (a) first language choice, then (b) a page choice, so I have the following routes
App.Router.map(function () {
this.resource('lang', { path: '/:lang_id' }, function() {
this.resource('page', { path: '/:page_id' });
});
});
Implemented by
App.ApplicationRoute = Em.Route.extend({
model: function() {
return this.store.find('lang');
}
});
App.LangIndexRoute = Em.Route.extend({
model: function(params) {
return this.store.find('lang', params.lang_id);
}
});
App.PageRoute = Em.Route.extend({
model: function(params) {
return this.store.find('page', params.page_id);
}
});
model is :
App.Lang = DS.Model.extend({
name: DS.attr('string'),
pages: DS.hasMany('page')
});
App.Page = DS.Model.extend({
lang: DS.belongsTo('lang'),
name: DS.attr('string'),
title: DS.attr('string'),
extra_title: DS.attr('string'),
});
App.Lang.FIXTURES = [
{
id: 'en',
name: 'EN',
pages: [1, 2]
},
{
id: 'fr',
name: 'FR',
pages: [3]
},
];
App.Page.FIXTURES = [
{
id: 'page-item1',
name: 'ITEM1',
title: 'ITEM1',
extra_title: 'item1',
},
{
id: 'page-item2',
name: 'ITEM2',
title: 'ITEM2',
extra_title: 'item2',
},
{
id: 'page-item3',
name: 'ITEM3',
title: 'ITEM3',
extra_title: 'item3',
},
];
And the following markup :
<script type="text/x-handlebars" >
<nav>
<ul>
{{#each}}
<li>{{#linkTo 'lang' this currentWhen="lang" activeClass="selected" }}{{name}}{{/linkTo}}</li>
{{/each}}
</ul>
</nav>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="lang/index">
<nav>
<ul class="projects">
{{#each pages}}
<li>{{#link-to "page" activeClass="selected"}}{{name}}{{/link-to}}</li>
{{else}}
<li>Loading...</li>
{{/each}}
</ul>
</nav>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="page">
</script>
I cannot get the pages listed for the given language to be enumerated when loading the lang.index template.
How could I do that, or would there be a better way to manage language with ember ?
I think my mistake (or misunderstanding of the framework) is either in the model hook or in the iterator of #each...
I unblock the situation by modifying the route, model and template as below.
One of the problem was due to the need of declaring the hasmany relationship as async. (using FIXTURES), the other were related to the linkTo in the template.
However I still don't have exactly the behavior I want in the pages/index template. All pages are rendered, instead of only the one of the given language.
App.Router.map(function () {
this.resource('pages', { path: '/:lang_id' }, function() {
this.route('page', { path: '/:page_id' });
});
App.ApplicationRoute = Em.Route.extend({
model: function() {
return this.store.find('lang');
}
});
App.PagesIndexRoute = Em.Route.extend({
model: function(params) {
console.log(params.lang_id);
return this.store.find('page', params.lang_id);
}
});
App.Lang = DS.Model.extend({
name: DS.attr('string'),
pages: DS.hasMany('page',{async:true})
});
<script type="text/x-handlebars" >
<nav>
<ul>
{{#each}}
<li>{{#linkTo 'pages' this currentWhen="pages" activeClass="selected" }}{{name}}{{/linkTo}}</li>
{{/each}}
</ul>
</nav>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="lang/index">
<nav>
<ul class="projects">
{{#each pages}}
<li>{{#link-to "pages.page" this activeClass="selected"}}{{name}}{{/link-to}}</li>
{{else}}
<li>Loading...</li>
{{/each}}
</ul>
</nav>
{{outlet}}
</script>