Ember How can i render a child route into his parent? - ember.js

I'm new with Ember and trying to render the content into the category template. So, if I clicked on the category, it will show me details and list content in the category template. I have tested something, but it didn't work. I have searched for this problem, but I can't solve it. I hope you can help me.
best regards
app.js
TableNotices.Router.map(function() {
this.resource('tableNotices', { path: '/' }, function(){
this.resource('category', {path: ':id'}, function(){
this.route('contents');
this.resource('content', {path: '/content/:id'});
});
});
});
TableNotices.ContentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('category').get('contents');
}
});
TableNotices.Content = DS.Model.extend({
content: DS.attr('string'),
contentType: DS.attr('string'),
orderPos: DS.attr('number'),
category: DS.belongsTo('category')
});
TableNotices.Category = DS.Model.extend({
name: DS.attr('string'),
parent: DS.attr('number'),
picture: DS.attr('string'),
contents: DS.hasMany('content', {async:true})
});
index.html:
<script type="text/x-handlebars" data-template-name="category">
{{name}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="contents">
foobar
</script>
jsbin

In the route you can specify how to render the templates with renderTemplate.
TableNotices.ContentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('category').get('contents');
}
renderTemplate: function() {
this.render('contents', { // the template to render
into: 'category', // the template to render into
outlet: 'category', // the name of the outlet in that template
controller: 'contents' // the controller to use for the template
});
}
});
<script type="text/x-handlebars" data-template-name="category">
{{name}}
{{outlet "contents"}}
</script>

Although Aaron Renoir's answer will work, the best way to think of this is that because your routes are nested, your templates need to be nested as well. Ember will render each of the nested templates into the parent template when these nested routes are activated. This gives you a nice way to delegate different parts of your page to various templates which helps to keep the content organized.
Each of your "parent" templates will need an {{outlet}} for the child templates to be rendered into.

Related

Filling Ember selection with data received from server

I would like to receive json data from server and then generate html selection tag in Ember. This is my handlebars template:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="rankings">
{{view Ember.Select contentBinding="countries" optionLabelPath="countries.name" optionValuePath="countries.path"}}
</script>
and my Ember trunk
App.ApplicationAdapter = DS.RESTAdapter;
App.Router.map(function() {
this.route('rankings')
});
App.RankingsRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('countries', this.store.findAll('country'));
}
});
App.RankingsController = Ember.Controller.extend({
countries: []
});
App.Country = DS.Model.extend({
path: DS.attr('string'),
name: DS.attr('string'),
}
It is not working fine. I am not having any errors in console and template is not rendering with this route and controller. What can be wrong?
Ember.Select looks for data on your controller's model. You have the countries array set up outside of your content, so it will not be able to view the data.
Personally, I would do it in the following way, though there are a couple of different approaches you can take to get similar results:
App.ApplicationAdapter = DS.RESTAdapter;
App.Router.map(function() {
this.route('rankings')
});
App.RankingsRoute = Ember.Route.extend({
model: function() {
return {countries : this.store.findAll('country')};
}
});
App.RankingsController = Ember.Controller.extend({
});
App.Country = DS.Model.extend({
path: DS.attr('string'),
name: DS.attr('string'),
}

Ember.js - setupController model no output

Ey guys,
I have a strange problem with one of my models:
The following code is responsible for getting the page model and all the new projects.
Bouwbedrijf.ProjectsOldRoute = Ember.Route.extend({
model: function() {
return App.Page.find(3);
},
setupController: function(controller, model)
{
controller.set('content', model);
controller.set('projecten', App.OldProjects.find());
},
renderTemplate: function (){
this.render();
this.render('pagetitle', {into: 'projects', outlet: 'page-title'});
this.render('oldprojectshowcase', {
into: 'projects/old',
outlet: 'projecten'
});
}
});
However if I loop through the controller.projecten within the template there is no propery output. I do see 3 li elements rendered but I can't seem to display the name of a project...
Here is the template code
<script type="text/x-handlebars" data-template-name="oldprojectshowcase">
<h3>Projects showcase</h3>
<ul>
{{#each project in projecten}}
<li>{{project.name}}</li>
{{/each}}
</ul>
</script>
To make things more clear for you guys I prepared a jsbin -> http://jsbin.com/Uqop/2/
Within this example there is a page Old projects which is displayin a showcase (sort of ;) )
There is no relation between the problem and setupController.
The problem is you are trying to access a property which you have not defined in the model.
The property name is not defined in the App.OldProjects model.
Defining it does the trick.
App.OldProjects = DS.Model.extend({
title: DS.attr('string'),
project: DS.attr('string'),
name: DS.attr('string'),
img: DS.attr('string')
});
Corrections to your JSBin.

Binding model to template with emberjs

I am going to bind model to template with emberjs
<script type="text/x-handlebars" id="dashboard">
<div>
<span>this is user list</span>
<div>
{{render userinfo userinfo}}
</div>
</div>
</script>
<script type="text/x-handlebars" id="_userinfo">
{{#each model}}
<span>{{user}}
{{/each}}
</script>
App.Userinfo= DS.Model.extend({
user: DS.attr("string")
});
App.Userinfo.FIXTURES = [
{user:"user1"},
{user:"user2"},
{user:"user3"}
];
App.UserinfoView= Ember.View.extend({
});
App.UserinfoController = Ember.ObjectController.extend({
});
App.Router.map(function() {
this.resource('dashboard', {path: '/dashboard'}, function() {
});
});
App.DashboardRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('dashboard', { // the template to render
controller: 'dashboard' // the controller to use for the template
});
}
});
App.DashboardController = Ember.ObjectController.extend({
});
When i go to /#/dashboard, Dashboard template is loaded.
In here, I have rendered userinfo.
I'd like to bind Userinfo Model to usersinfo template so that I display all users.
Help me, please.
The short: here a working jsbin.
The long: You hade slightly to much unnecessary going on in your code, basically this does the job:
First of all you had no redirect to your dashboard route, since it's your only route (at least as far I can see from your code) we redirect directly to it after entering the index route
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('dashboard');
}
});
I've removed the DashboardController since there is nothing to be done.
Then your DashboardRoute was missing the model hook to provide actually data for your dashboard template
App.DashboardRoute = Ember.Route.extend({
model: function(){
return App.Userinfo.find();
}
});
In the router map you don't need to define a path if the URL is the same name as your template name in your case it is the same dashboard
App.Router.map(function() {
this.resource('dashboard');
});
The userinfo model was correct
App.Userinfo= DS.Model.extend({
user: DS.attr("string")
});
But your fixtures where missing the id
App.Userinfo.FIXTURES = [
{id:1, user:"user1"},
{id:2, user:"user2"},
{id:3, user:"user3"}
];
Moreover you where using the render helper with a partial template _userinfo the correct way to render a partial is this
{{partial userinfo}}
As you can see we don't pass any additional parameters to it because the data will be made available trough your model hook. The partial helper uses the context and the data provided in the template it is rendered into, in your case the dashboard template, therefore the model hook is necessary.
Hope it helps.

Emberjs Application Routes and Controllers

Can someone explain why this works:
Code in App.js:
App.ApplicationRoute = Ember.Route.extend({
setupController : function (params) {
this.controllerFor('food').set('model', App.Food.find(params.food_id));
}
});
But the following won't, unless I explicitly declare App.FoodController = Ember.ObjectController.extend();
App.FoodRoute = Ember.Route.extend({
model : function(params) {
return App.Food.find(params.food_id);
}
});
This is the code I'm using in index.html and does not change between blocks of code
<script type="text/x-handlebars" data-template-name="application">
{{ outlet }}
</script>
<script type="text/x-handlebars" data-template-name="food">
{{name}}
</script>
Router:
App.Router.map(function() {
this.resource( 'foods' );
this.resource( 'food', { path : '/food/:food_id' } );
});
The code that you have shown seems OK. Here is a working fiddle that proves it:
http://jsfiddle.net/ebXeS/2/
The only thing wrong about the code is this part (which is excluded from the fiddle):
App.ApplicationRoute = Ember.Route.extend({
setupController : function (params) {
this.controllerFor('food').set('model', App.Food.find(params.food_id));
}
});
According to your Router definition, you should not have food_id in the parameters of your application route. More than that, you should access the controller for the food route in the uhm... FoodRoute. Read more about Ember and the way it does routing (http://emberjs.com/guides/).

createRecord called w/o params does not add object to collection

Using:
ember-1.0.0-pre.4.js
ember-data.js REVISION:11
handlebars-1.0.rc.2.js
Please have a look at this jsFiddle illustrating the described problem.
I have a list of items that are displayed in a template. The template contain a linkTo helper that let's the controller add an item to the collection and is shown as a text input on the page.
Adding the item to the collection is done by the controller:
App.TodoItem = DS.Model.extend({
title: DS.attr('string', { defaultValue: "unknown" })
});
App.Router.map(function () {
this.resource('todo_items')
});
App.TodoItemsRoute = Em.Route.extend({
model: function () {
return App.TodoItem.find();
}
});
App.TodoItemsController = Em.ArrayController.extend({
addTodoItem: function () {
App.TodoItem.createRecord();
}
});
If I want the new item to be shown is the list, I have to pass params to createRecord, otherwise the item is not visible. The same behaviour can be reproduced by using Chrome's inspector and then the item can be made visible as follows:
// Open the jsFiddle http://jsfiddle.net/bazzel/BkFYd/ and select 'result(fiddle.jshell.net) in the inspector, then:
var item = App.TodoItem.createRecord();
// Nothing visible yet.
item.set('title', 'Whatever');
// Now the text input appear with the title as its value.
Is this expected behaviour and if so, what am I missing here?
I took time to redo your example the way i feel things should be done properly with Emberjs. You should rather make sure of transaction and properly define your views and then all your issues get taken care of. So here's how i think you should do this
Define a view for the textfield to capture the value being entered or
just bind it to the model property.
Listing items and adding a new item to the list should be done in two different views and should not be mixed together
<script type="text/x-handlebars">
{{outlet}}
<div>
{{outlet 'addItem'}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="todo_items">
{{#linkTo 'todo_items.new'}}Add Todo Item{{/linkTo}}
<ul>
{{#each item in controller}}
<li>
{{#unless item.isNew}}
{{item.title}}
{{/unless}}
</li>
{{/each}}
</ul>
</script>
Define different states for listing items and adding a new one
To benefit from automatic binding of your text field value to the
model property, you need to associate an ObjectController to the TodoItemsNew route
Finally, make use of transaction to create and commit records to the store
window.App = Em.Application.create();
App.TodoItem = DS.Model.extend({
title: DS.attr('string')
});
App.TodoItem.FIXTURES = [{
id: 1,
title: 'Lorem'
}, {
id: 2,
title: 'Ipsum'
}];
App.store = DS.Store.create({
revision: 11,
adapter: DS.FixtureAdapter.create()
});
App.Router.map(function () {
this.resource('todo_items',function(){
this.route('new');
})
});
App.IndexRoute = Em.Route.extend({
redirect: function () {
this.transitionTo('todo_items');
}
});
App.TodoItemsRoute = Em.Route.extend({
model: function () {
return App.TodoItem.find();
}
});
App.TodoItemsNewRoute = Em.Route.extend({
transaction: App.store.transaction(),
setupController:function(controller) {
console.info(controller.toString());
controller.set('content',this.transaction.createRecord(App.TodoItem));
},
renderTemplate: function() {
this.render('addItem',{
into:'application',
outlet:'addItem',
})
},
events: {
addItem: function() {
this.transaction.commit();
this.transitionTo('todo_items');
}
}
});
App.TodoItemsController = Em.ArrayController.extend();
App.TodoItemsNewController = Em.ObjectController.extend();
App.TextField = Ember.TextField.extend({
insertNewline: function () {
this.get('controller').send('addItem')
}
});
Here' is a working version of the example on jsfiddle. Hopefully, i helped with this example clarify some of your issues.
Thank you Ken for answering my question. It indeed feels like a more proper of way of doing this in Ember. However, I still think it's difficult to get the hang of which objects are accessible from where...
Your example inspired me to do a rewrite of my code. I also made some changes to your approach:
I'm not sure if it's the best practice, my I don't create a store instance. Instead I define a Store class.
The content for the TodoItemsNewController is set by calling the model property on the corresponding route.
renderTemplate in the TodoItemsNewRoute only needs the outlet key.
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="todo_items">
{{#linkTo 'todo_items.new'}}Add Todo Item{{/linkTo}}
<ul>
{{outlet "addItem"}}
{{#each controller}}
<li>
{{#unless isNew}}
{{title}}
{{/unless}}
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="todo_items/new">
{{view Ember.TextField valueBinding="title" placeholder="Enter title"}}
window.App = Em.Application.create();
App.TodoItem = DS.Model.extend({
title: DS.attr('string', {
defaultValue: "unknown"
})
});
App.TodoItem.FIXTURES = [{
id: 1,
title: 'Lorem'
}, {
id: 2,
title: 'Ipsum'
}];
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.FixtureAdapter.create()
});
App.Router.map(function() {
this.resource('todo_items', function() {
this.route('new');
});
});
App.IndexRoute = Em.Route.extend({
redirect: function() {
this.transitionTo('todo_items');
}
});
App.TodoItemsRoute = Em.Route.extend({
model: function() {
return App.TodoItem.find();
}
});
App.TodoItemsNewRoute = Em.Route.extend({
model: function() {
return App.TodoItem.createRecord();
},
renderTemplate: function() {
this.render({
outlet: 'addItem'
});
}
});
App.TodoItemsNewView = Em.View.extend({
tagName: 'li'
});
The updated example is on jsFiddle.
Any reviews are welcome.