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.
Related
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.
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'),
}
I'm new to emberJS and I'm having some trouble working with promises.
Here is my router:
this.resource('menus', function(){
this.resource('menu', {path: '/:menu_id'}, function(){
this.resource('submodule', {path: '/:submodule_id'});
});
});
});
I have nested routes, and the child route returns a menuss object based on a given id.
Here is my MenuRoute:
App.MenuRoute = Ember.Route.extend({
model: function(params){
return this.store.find('menuss', params.menu_id);
}
});
Here are my models:
App.Menuss = DS.Model.extend({
name: DS.attr('string'),
subModule: DS.hasMany('submodule', {async:true})
});
App.Submodule = DS.Model.extend({
name: DS.attr('string'),
content: DS.attr('string')
});
The 'subModule' attribute of Menuss model contains an array of Submodule model id's.
Inside my menu template, I'm receiving a menuss object and I want to display the SubModules each menu item has.
However, when I call {{this.subModule}}, it returns <DS.PromiseArray:ember488>. How can I get the contents from this subModule array?
I looked at some similar questions where they say use the then() method, but I can't seem to figure it out here.
In the template you'll want to iterate the property since it's an array, ember/handlebars will deal with the synchronicity of the PromiseArray.
{{#each item in subModule}}
{{item.name}}
{{/each}}
I see many confusing examples in Ember.js official tutorials.
One example which I really don't like is:
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('title', "Hello world!");
}
});
App.ApplicationController = Ember.Controller.extend({
appName: 'My First Example'
});
Now as I understand it I could have written it like that instead:
App.ApplicationController = Ember.Controller.extend({
appName: 'My First Example',
title: 'Hello world!'
});
And removing the setupController from route.
What is the purpose/benefit of using setupController?
setupController is primarily for setting up some controller context dynamically. In your example, if the title is always gonna be "Hello world!" it's fine to set it in class declaration.
By default, setupController will set the model property of controller to the value returned from model hook of the route.
You could also you it to, for example, set the model of another controller, or set some initial controller state that depends on the model.
For example, suppose you have the following:
// Model
App.Post = DS.Model.extend({
title: DS.attr('string'),
text: DS.attr('string'),
autoEdit: DS.attr('string')
});
// Controller
App.PostController = Ember.ObjectController.extend({
isEditing: null,
toggleEdit: function() { this.toggleProperty('isEditing'); }
});
Template:
<a href="#" {{action 'toggleEdit'}}>Toggle edit mode</a>
{{#if isEditing}}
{{input type="text" value=title placeholder="Title"}}
{{textarea type="text" value=text placeholder="Text"}}
{{else}}
<h1>{{title}}<h1>
<article>{{text}}</article>
{{/if}}
And then, you decide that it would be nice to turn editing mode on by default for posts with autoEdit equal to true. You'll probably want to do that in the route (since the controller, when instantiated, knows nothing about the model):
App.PostRoute = Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller, model);
if (model.get('autoEdit')) {
controller.set('isEditing', true);
}
}
});
So basically, it's for "initializing" the controller (setting model and default state).
I'm playing with emberjs for a few days now and thought I understand the basics. Now I'm trying to build a real world application and already got stuck with a strange problem.
I want to have an application template that uses the 'view' helper to include some other views. I also want to have some 'global' models that contain information about the environment (e.g. the logged in users account information etc.) that I want to be able to use in different views.
I tried this: http://jsfiddle.net/UzgMd/7/
<script type="text/x-handlebars" data-template-name="header">
<div>Header {{fullName}}</div>
</script>
<script type="text/x-handlebars" data-template-name="sidebar">
<div>Sidebar</div>
</script>
<script type="text/x-handlebars" data-template-name="application">
<div>{{view App.HeaderView}}</div>
<div>{{view App.SidebarView}}</div>
</script>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="https://raw.github.com/wycats/handlebars.js/1.0.0-rc.3/dist/handlebars.js"></script>
<script type="text/javascript" src="https://raw.github.com/emberjs/ember.js/release-builds/ember-1.0.0-rc.2.js"></script>
<script type="text/javascript" src="https://raw.github.com/MilkyWayJoe/cdnjs/master/ajax/libs/ember-data.js/0.12.00-latest20130328/ember-data-latest.js"></script>
with the following JS:
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
App.AccountInfo = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.AccountInfo.FIXTURES = [{
id: 1,
firstName: "John",
lastName: "Doe"
}];
//App.headerController = Ember.ObjectController.extend({}).create(); // works
App.HeaderController = Ember.ObjectController.extend({}); // doesnt work
App.HeaderView = Ember.View.extend({
templateName: 'header',
//controller: App.headerController // works
controller: App.HeaderController.create() // doesnt work
});
App.SidebarView = Ember.View.extend({
templateName: 'sidebar'
});
App.ApplicationController = Ember.Controller.extend({});
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return App.AccountInfo.find(1);
},
setupController: function(controller, model) {
this._super(controller, model);
//App.headerController.set('model', model); // works
this.controllerFor('header').set('content', model); // doesnt work
}
});
but It doesn't work because the controllerFor()-method in line 47 returns a new instance of the HeaderController instead of the instance already created in line 30. If I call controllerFor() again, no new instance is created but the instance created by the call in line 47 is returned - what I find a bit confusing.
However, if I do it like this: http://jsfiddle.net/UzgMd/8/
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
App.AccountInfo = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.AccountInfo.FIXTURES = [{
id: 1,
firstName: "John",
lastName: "Doe"
}];
App.headerController = Ember.ObjectController.extend({}).create(); // works
//App.HeaderController = Ember.ObjectController.extend({}); // doesnt work
App.HeaderView = Ember.View.extend({
templateName: 'header',
controller: App.headerController // works
//controller: App.HeaderController.create() // doesnt work
});
App.SidebarView = Ember.View.extend({
templateName: 'sidebar'
});
App.ApplicationController = Ember.Controller.extend({});
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return App.AccountInfo.find(1);
},
setupController: function(controller, model) {
this._super(controller, model);
App.headerController.set('model', model); // works
//this.controllerFor('header').set('content', model); // doesnt work
}
});
using a singleton controller instance in that I inject the model, everything works fine.
Now my questions:
Is the controllerFor() creating a new instance a bug? If not, why does it behave like this or what am I doing wrong?
Is the way I worked around in my second fiddle a proper way to do it, or is there a better way?
I want to have an application template that uses the 'view' helper to include some other views.
The {{view}} helper is appropriate if all you want is a simple Ember.View with no controller. For example something like Ember.InputField. For an application's header and sidebar you'll typically want a view backed by a controller. In that case use the {{render}} helper instead. Have a look at this blog post for more details.
I also want to have some 'global' models that contain information about the environment (e.g. the logged in users account information etc.) that I want to be able to use in different views.
Makes sense. The right place to store that information is in a controller. That might be a property of ApplicationController or in a separate controller like currentUserController. Since a view should only access properties of it's own controller, you can use use controller's needs array to make it accessible. See managing dependencies between controllers
Is the controllerFor() creating a new instance a bug?
No, it is not a bug.
If not, why does it behave like this or what am I doing wrong?
Issue is that you are trying to manually create instances of headerController. In general if you are calling create() on a controller then something is wrong. Ember expects to manage controller instances itself, and will register them on the container so they can be found later. Since your instance was not registered, ember created a new one when you called controllerFor().
Is the way I worked around in my second fiddle a proper way to do it, or is there a better way?
Definitely not the proper way. In this fiddle you are using a global variable to accomplish what ember should be doing for you automatically. That means there is a lot of code that is not necessary, and also there is a lot of coupling between objects which makes things hard to test. If you go with the {{render}} approach it would look something like this:
<script type="text/x-handlebars" id="application">
{{render header}}
{{render sidebar}}
{{outlet}}
</script>
<script type="text/x-handlebars" id="header">HEADER {{content}}<hr/></script>
<script type="text/x-handlebars" id="sidebar">SIDEBAR {{content}}<hr/></script>
App = Ember.Application.create({});
App.HeaderController = Ember.ObjectController.extend();
App.SidebarController = Ember.ObjectController.extend();
App.IndexRoute = Ember.Route.extend({
setupController: function() {
this.controllerFor('header').set('content', 'hi');
this.controllerFor('sidebar').set('content', 'bye');
}
});
Using {{render}} we don't need to define HeaderView or SidebarView since ember will generate them automatically. Each will be bound to a singleton instance of their controller, and those controllers can be accessed from your routes via controllerFor.
JSFiddle here: http://jsfiddle.net/NQKvy/1/