Handling Two hasMany Relationships - ember.js

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.

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>

Ember: Rendering nested loops from multiples related models

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

Can't get it work 'ember-data'

App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
App.Router.map(function() {
this.resource('about')
this.resource('posts')
});
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
// i tried return App.Post.find(); didn't work.
}
});
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.attr('string'),
intro: DS.attr('string'),
extended: DS.attr('string'),
publishedAt: DS.attr('date')
});
App.Post.FIXTURES = [{
id: 1,
title: "Title 1",
author: "Anonim",
publishedAt: new Date('21-04-2014'),
intro: "Introduction",
extended: "It is a long story"
}, {
id: 2,
title: "Title 2",
author: "Anonymous 2",
publishedAt: new Date('12-04-2014'),
intro: "Introduction for ID 2",
extended: "It is a long story for ID 2"
}];
And handlebars (I wll try to use hbs next time)
<script type="text/x-handlebars">
<nav class="top-bar" data-topbar>
<ul class="title-area">
<li class="name">
<h1>Starter Kit</h1>
</li>
<li class="toggle-topbar menu-icon">Menu</li>
</ul>
<section class="top-bar-section">
<ul class="left">
<li>{{#link-to 'posts'}}Posts{{/link-to}}</li>
<li>{{#link-to 'about'}}About{{/link-to}}</li>
</ul>
</section>
</nav>
{{outlet}} <!--it is a placeholder to show templates-->
</script>
<script type="text/x-handlebars" id="posts">
<div class="row">
<div class="large-12 columns">
<h3>Posts Page</h3>
<h4>Dynamic Posts Page</h4>
{{#each model}}
<ul>
<li>
<p>{{title}}</p>
<p>{{author}}</p>
<p>{{intro}}</p>
<p>{{extended}}</p>
<p>{{date}}</p>
</li>
</ul>
{{/each}}
</div>
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" id="about">
<div class="row">
<div class="large-12 columns">
<h3>About page</h3>
<p>About</p>
</div>
</div>
{{outlet}}
</script>
The error i get
GET http://localhost/posts 404 (Not Found) jquery.js:26
Error while loading route: undefined
After reading introductions both on ember.js offical site and other community sites, I tried to follow the ember guide following by this link, however, i couldn't make it work.
You should assign DS.FixtureAdapter itself rather than a string. You can also remove the revision property since that's not really necessary anymore.
Change:
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
To:
App.Store = DS.Store.extend({
adapter: DS.FixtureAdapter
});
Your error explains exactly why it's not working. The GET request failed because http://localhost/posts doesn't exist. You should read more about Ember's adapters and their conventions.

How can I trigger a commit/save?

Users can be manipulated through form fields. But I can't figure out how I can save (commit) them. When ever I change the lastName of a User and click the save button I'll get the following error:
Uncaught Error: Attempted to handle event `save` on <App.User:ember309:1> while in state rootState.loaded.updated.uncommitted. Called with undefined
What do I have to change to save (commit) a record by clicking on the save button?
index.html
<script type="text/x-handlebars" data-template-name="users">
<table class='table'>
{{#each model}}
<tr>
<td>{{view Ember.TextField valueBinding='lastName'}}</td>
{{#with this as model}}
<td><button {{bindAttr class=":btn isDirty:btn-primary:disabled"}} {{action "save" target="model"}}>save</button></td>
{{/with}}
</tr>
{{/each}}
</table>
</script>
app.js
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
})
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' })
})
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.UserController = Ember.ObjectController.extend({
save: function() {
this.commit();
}
})
App.User = DS.Model.extend({
lastName: DS.attr('string')
})
App.User.FIXTURES = [{
id: 1,
lastName: "Smith"
}]
You can change your code like this
<script type="text/x-handlebars" data-template-name="users">
<table class='table'>
{{#each model in controller}}
<tr>
<td>{{view Ember.TextField valueBinding='lastName'}}</td>
<td><button {{bindAttr class=":btn isDirty:btn-primary:disabled"}} {{action "save"}}>save</button></td>
</tr>
{{/each}}
</table>
</script>
that uses each model in controller and not #with this as model
And you define then an itemController
App.UsersController = Ember.ArrayController.extend({
itemController: 'user'
});
This will allow you to have the save method of the UserController called.
Finally, make sure you save your record:
App.UserController = Ember.ObjectController.extend({
save: function() {
this.get('content.transaction').commit();
}
})

Mark the current detail entry in a master list

I have a list of users which are displayed in a master view on the left side (Twitter Bootstrap CSS). Details of each user can be shown by clicking the show button. They will be displayed on the right side (detail).
How can I remove the show button for the currently displayed user? e.g. #/users/1 shouldn't render the show button for the first user.
index.html
<script type="text/x-handlebars" data-template-name="users">
<div class='row'>
<div class='span4'>
<table class='table table-striped'>
{{#each model}}
<tr>
<td>{{lastName}}</td>
<td>{{#linkTo 'user' this}}<button class="btn" type="button">show</button>{{/linkTo}}</td>
</tr>
{{/each}}
</table>
</div>
<div class='span8'>
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="user">
<h1>{{firstName}} {{lastName}}</h1>
</script>
app.js
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
})
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' })
})
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
})
App.User.FIXTURES = [{
id: 1,
firstName: "Bill",
lastName: "Clinton"
}, {
id: 2,
firstName: "Barack",
lastName: "Obama"
}]
Ember provides some support for doing what you want. By default it sets the "active" css class on the selected element. You can find more information about that here: http://emberjs.com/api/classes/Ember.LinkView.html (note that the {{#linkTo}} is just a helper based on the LinkView).
The simplest way to override this behavior, since instead of "active" you want to hide the button, would be to make use of the hide class that comes with Twitter Bootstrap. So your users template would look like:
<script type="text/x-handlebars" data-template-name="users">
<div class='row'>
<div class='span4'>
<table class='table table-striped'>
{{#each model}}
<tr>
<td>{{lastName}}</td>
<td>{{#linkTo 'user' this activeClass="hide"}}<button class="btn" type="button">show</button>{{/linkTo}}</td>
</tr>
{{/each}}
</table>
</div>
<div class='span8'>
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="user">
<h1>{{firstName}} {{lastName}}</h1>
</script>