Pass Iterating Object to Controller Method - ember.js

When trying to pass an iterating object to the Controller method, the object is passed as a String.
Template:
<script type="text/x-handlebars" data-template-name="gradebooks">
{{#each}}
{{#link-to "gradebook" this}}{{title}}{{/link-to}}
{{/each}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="gradebook">
<h1>Student Grades</h1>
{{#each student in students}}
{{#each assignment in assignments}}
{{student.name}}, {{assignment.name}}: {{getGrade student assignment}}%
{{/each}}
{{/each}}
</script>
Routes:
App.Router.map(function() {
this.resource('gradebooks', function() {
this.resource('gradebook', { path: ':gradebook_id' });
})
});
App.GradebooksRoute = Ember.Route.extend({
model: function() {
return this.store.find(App.Gradebook);
}
});
App.GradebookRoute = Ember.Route.extend({
model: function(params) {
return this.store.find(App.Gradebook, params.gradebook_id);
}
});
Controller:
App.GradebookController = Ember.ObjectController.extend({
getGrade: function(student, assignment) {
console.log(student, assignment);
return 5;
}
});
Model:
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({
name: DS.attr('string'),
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, the above outputs the string "student" for each student. If I were to have {{getGrade student.id}}, it would output the string "student.id". How could I get it to pass the student as an object?

Given your revision I've revised my answer below to better align:
App.AssignmentController = Ember.ObjectController.extend({
needs: ["student"],
getGrade: function() {
var student = this.get('controllers.student');
//logic to get grade
}.property() //thing to observe for changes
});
<script type="text/x-handlebars" data-template-name="gradebooks">
<h1>Student Grades</h1>
{{#each}}
{{#link-to "gradebook" this}}{{title}}{{/link-to}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="gradebook">
{{#each student in students}}
{{render 'student' student}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="student">
{{name}}
{{#each assignment in assignments}}
{{render 'assignment' assignment}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="assignment">
{{name}}: {{getGrade}}%
</script>
A useful tool is {{log model}} or {{log record}} or {{log ...whatver..}} to understand what data your view is receiving.
If you open the Chrome Ember Inspector you should also see what views, controllers, routes, models, etc. have been defined which will help with understanding.
In short you cannot pass parameters to controllers by using {{getGrade student.id}} - you can define a Handelbars helper to accept parameters but I don't think this is what you are after. The method above worked for me.
Hope this helps

Related

Ember.js: programmatically load hasmany relationship

Disclamer: I started using Ember.js few days ago
Suppose having these 2 models:
App.Question = DS.Model.extend({
text: DS.attr('string'),
answers: DS.hasMany('answer', {async: true})
});
App.Answer = DS.Model.extend({
question: DS.belongsTo('question'),
text: DS.attr('string')
});
For some reasons I got this JSON (without "answers" list)
{
...
"questions": [
{"id": 1, "text": "Foo?"}, ...
]
}
In the template I want to load and show answers only if explicitly needed
{{#each questions itemController="question"}}
<div class="answer-wrapper">
{{text}}
<button {{action "loadAnswers"}}>Load answers</button>
<ul>
{{#each answers}}
<li>{{text}}</li>
{{/each}}
</ul>
</div>
{{/each}}
How can I do this in the controller's loadAnswer action?
App.QuestionController = Ember.ObjectController.extend({
...
actions: {
loadAnswers: function() {
// programatically load answers
}
}
});
Workaround: I can do this changing the attribute in the template
{{#each loadedAnswers}}
<li>{{text}}</li>
{{/each}}
and defining correctly the action
App.QuestionController = Ember.ObjectController.extend({
...
actions: {
loadAnswers: function() {
// or loaded with ajax...
this.set("loadedAnswers", [
{id: 1, text: "foo"},
{id: 2, text: "bar"}
]);
}
}
});
You can wrap that portion of the template with an if statement. The async relationship won't be fetched unless it's requested.
Controller
App.QuestionController = Ember.ObjectController.extend({
showAnswers: false,
...
actions: {
loadAnswers: function() {
this.set('showAnswers', true);
}
}
});
Template
{{#if showAnswers}}
<ul>
{{#each answers}}
<li>{{text}}</li>
{{/each}}
</ul>
{{/if}}

Ember.js link-to inside #each block does not render

<script type="text/x-handlebars" data-template-name="application">
<ul>
{{#each person in controller}}
<li>{{#link-to 'foo' person}}{{person.firstName}}{{/link-to}}</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="foo">
<h5>Foo route</h5>
{{name}}: converted to {{fullName}}
</script>
javascript:
App = Ember.Application.create({});
App.ApplicationRoute = Ember.Route.extend({
model: function(){
return [
{firstName: 'Kris', lastName: 'Selden'},
{firstName: 'Luke', lastName: 'Melia'},
{firstName: 'Formerly Alex', lastName: 'Matchneer'}
];
}
});
App.Router.map(function() {
this.route('foo');
});
App.FooRoute = Ember.Route.extend({
model: function(params) {
// just ignore the params
return Ember.Object.create({
name: 'something'
});
}
});
App.FooController = Ember.ObjectController.extend({
fullName: function() {
return this.get('name') + ' Jr.';
}
});
I must be doing something wrong, because these {{#link-to}}'s are failing. Here's a JSBin. Hesitating to file an issue because this seems like such a simple thing:
http://jsbin.com/ucanam/4777/edit?html,js,output
Your route does not allow parameter. Change it to
this.resource('foo', { path: '/:person_id'});

template not bind a model in Ember

I will try to bind model,controller and template in ember
Here is my js
App = Ember.Application.create({});
App.Person = Ember.Object.extend({
firstName: "r",
lastName: "issa"
});
App.TestingRoute = Ember.Route.extend({
model: function () {
return App.Person.create();
},
setupController: function (controller, model) {
controller.set("model", model);
}
});
App.TestingController = Ember.ObjectController.extend({
submitAction: function () {
alert("My model is :" + this.get("model"));
}
});
my template is :
<script type="text/x-handlebars" data-template-name="application">
{{render testing}}
</script>
<script type="text/x-handlebars" data-template-name="testing">
{{input valueBinding="model.firstName"}}
{{input valueBinding="model.lastName"}}
<button {{action submitAction target="controller"}} class="btn btn-success btn-lg">Pseudo Submit</button>
<p>{{model.firstName}} - {{model.lastName}}</p>
</script>
what s wrong why model not binding in template and alert retunn model is null
Your setupController and model methods from TestingRoute aren't being called. Because your controller is created by the render view helper, not by a transition.
For example using the following works:
template
<script type="text/x-handlebars" data-template-name="testing">
{{input valueBinding="model.firstName"}}
{{input valueBinding="model.lastName"}}
<button {{action submitAction target="controller"}} class="btn btn-success btn-lg">Pseudo Submit</button>
<p>{{model.firstName}} - {{model.lastName}}</p>
</script>
javascript
App = Ember.Application.create({});
App.Router.map(function() {
this.route('testing', { path: '/' })
});
App.Person = Ember.Object.extend({
firstName: "r",
lastName: "issa"
});
App.TestingRoute = Ember.Route.extend({
model: function () {
return App.Person.create();
},
setupController: function (controller, model) {
debugger
controller.set("model", model);
}
});
App.TestingController = Ember.ObjectController.extend({
actions: {
submitAction: function () {
alert("My model is :" + this.get("model"));
}
}
});
Here is the fiddle http://jsfiddle.net/marciojunior/8DaE9/
Also, the use of actions in controllers is deprecated in favor of using the actions: { ... } object

how to create a right structure for nested routes in ember.js?

I need to create an application with routes:
/users - list of users
/users/123 - user info
/users/123/items - list of user items
/users/123/items/456 - items info
I wrote this code here
$(function() {
var App = window.App = Ember.Application.create({LOG_TRANSITIONS: true});
App.Router.map(function() {
this.resource("users", {path: '/users'}, function() {
this.resource("user", {path: '/:user_id'}, function() {
this.resource("items", {path: '/items'}, function() {
this.route("item", {path: '/:item_id'});
});
});
});
});
App.Store = DS.Store.extend({
revision: 11,
adapter: "DS.FixtureAdapter"
});
//ROUTES
App.UsersIndexRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.UsersUserIndexRoute = Ember.Route.extend({
model: function(params) {
return App.User.find(params.user_id);
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
//DATA
App.User = DS.Model.extend({
name: DS.attr('string'),
items: DS.hasMany('App.Item')
});
App.Item = DS.Model.extend({
name: DS.attr('string')
});
App.User.FIXTURES = [
{
id: 1,
name: 'Joe',
items: [1, 2]
}, {
id: 2,
name: 'John',
items: [2, 3]
}
];
App.Item.FIXTURES = [
{
id: 1,
name: 'Item 1',
}, {
id: 2,
name: 'Item 2',
}, {
id: 3,
name: 'Item 3',
}
];
return true;
});
And templates:
<script type="text/x-handlebars" data-template-name="application">
<div>
<h1>ember routes</h1>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="index">
<h2>Hello from index</h2>
{{#linkTo 'users'}}users area{{/linkTo}}
</script>
<script type="text/x-handlebars" data-template-name="users">
<h2>users area</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="users/index">
<h3>users list</h3>
{{#each user in controller}}
{{#linkTo "user" user}}{{user.name}}{{/linkTo}}<br/>
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="users/user">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="users/user/index">
<h4>user {{name}} index</h4>
</script>
<script type="text/x-handlebars" data-template-name="users/user/items">
<h4>user {{name}} items</h4>
{{outlet}}
</script>
If I go to /users , I see a list of users - its ok.
If I go to /users/1 I see message in browser console: "Transitioned into 'users.user.index'", but the content of the template 'users/user/index' is not showing.
I do not understand why, because I have App.UsersUserIndexRoute
Maybe I missed something?
if i'm not mistaken, using a UserIndexRoute (instead of a UsersUserIndexRoute - routes that are defined as resources usually don't have their parent routes' names prepended) with the model hook
model: function(params) {
return this.modelFor("user");
}
(and a corresponding template) should do the trick.

emberjs pre-4 and ember-data: no data on browser-refresh

If I browse from the index to a game, the data is shown, because of {{#linkTo}}s context, but if I'm refreshing the site, every time the game name doesn't show up.
EDIT: Here is a fiddle, but unfortunately the fiddle-version with the fixture adapter works properly, even with ken's suggestion to remove the model from the game-template.
the returned data from /api/games is as follows:
{
"games":[
{
"id":1,
"name":"First Game"
}
]
}
and the returned data from /api/games/1
{
"game": [
{
"id": 1,
"name": "First Game"
}
]
}
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="games">
<ul>{{#each game in controller}}
<li>{{#linkTo 'game' game}}{{game.name}}{{/linkTo}}</li>
{{/each}}</ul>
</script>
<script type="text/x-handlebars" data-template-name="game">
<h1>Name:{{model.name}}</h1>
</script>
<script type="text/javascript">
App = Ember.Application.create();
App.Game = DS.Model.extend({
name: DS.attr('string')
});
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.create({
namespace: 'api'
})
});
App.GamesController = Ember.ArrayController.extend({
content: []
});
App.GameController = Ember.ObjectController.extend({
content: null
});
App.Router.map(function(match) {
this.route("games", { path : "/" });
this.route("game", { path : "/games/:game_id" });
});
App.GameRoute = Ember.Route.extend({
model: function(params) {
return App.Game.find(params.game_id);
}
});
App.GamesRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', App.Game.find());
}
});
</script>
Has anyone an idea?
Remove model from your game template, try this instead
<script type="text/x-handlebars" data-template-name="game">
<h1>Name:{{name}}</h1>
</script>
JSFiddle your code to see where the problem is
The response from my server for a single game was wrong. I had an array with a single object in it, but I used the Object controller, so I fixed it with just responding with
{
"game": {
"id": 1,
"name": "First Game"
}
}