Ember: controller properties not available in partial views - what am I doing wrong? - ember.js

See JSFiddle: http://jsfiddle.net/cyclomarc/aYmuJ/3/
I set a property in the application controller and want to display this property in a partial view. This does not seem to work. I can access the property in the template itself, but not in the partial view rendered within the template ..
index.html
<script type="text/x-handlebars">
<h3>Ember access to controller properties</h3>
{{#linkTo 'about'}}About{{/linkTo}} <br><br>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="about">
Access to property in index template: <br>
<b>{{controllers.application.applicationVersion}}</b>
<br><br>
{{render "_footer"}}
</script>
<script type="text/x-handlebars" data-template-name="_footer">
Footer text (partial view) with a controller property:<br>
<b>{{controllers.application.applicationVersion}} MISSING</b>
</script>
app.js
var App = Ember.Application.create({});
App.Router.map(function () {
this.resource('about', { path: "/about" });
});
App.IndexRoute = Ember.Route.extend({
redirect: function () {
this.transitionTo('about');
}
});
App.ApplicationController = Ember.Controller.extend({
//Set some properties
applicationVersion: "1.0.0"
});
App.AboutController = Ember.Controller.extend({
needs: "application"
});

render view helper have your own context.
To use the current context in a other template use the partial view helper.
{{partial "footer"}}
When you use render a new controller is created, in that case named generated _footer controller.
Using partial will preserve the controller bound to the template that called the partial template
And since you used needs in about controller, you don't have it in the new generated controller.
Here is a sample

Related

Ember.js Rendering default content to a nested outlet

How do I render default content into a nested outlet?
For example, if I have an index template such as this:
<script type="text/x-handlebars" id="index">
<div>{{outlet}}</div>
</script>
<script type="text/x-handlebars" id="photo">
Photo!
</script>
<script type="text/x-handlebars" id="default">
Default photo
</script>
And a nested routes:
App.Router.map(function() {
this.resource('index', { path: '/'}, function() {
this.resource('default');
this.resource('photo', { path: ':id' });
});
});
That works fine when I use to link-to helper to load the page into the outlet. However, I cannot work out how to render default content into the outlet when the page first loads.
If I do something like this:
App.IndexRoute = Ember.Route.extend({
renderTemplate: function(controller, model) {
this._super(controller, model);
this.render('default');
},
});
It renders the default content into the main outlet. If I try to specify a named outlet instead:
this.render('default', { outlet: 'centre' });
I get the following error message:
Error while processing route: index.index Assertion Failed: An outlet (centre) was specified but was not found. Error: Assertion Failed: An outlet (centre) was specified but was not found.
Even when using a named outlet:
{{outlet "centre"}}
Any help appreciated.
Remove the index resource, it's already created for you and will make things confusing. Also, if you're needing to hook renderTemplate this early in the game, you're probably not following Ember's conventions.
I would also suggest removing the default resource, as Ember provides that by way of index. The top template is application.hbs, which essentially just has an {{outlet}} in it. So in summary:
Delete the index template
Change id="default" to id="index"
Remove the index resource from your router map
Thanks everyone, I used oshikryu's solution.
Templates:
<script type="text/x-handlebars" id="index">
<div>{{outlet}}</div>
</script>
<script type="text/x-handlebars" id="photo">
Photo!
</script>
<script type="text/x-handlebars" id="default">
Default photo
</script>
JavaScript:
App.Router.map(function() {
this.resource('index', { path: '/'}, function() {
this.resource('photo', { path: ':id' });
});
});
App.IndexIndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('default');
}
});

Inheriting singular controller with render helper

I am trying to render a set of tabs for a set of objects (conversations) using the render helper for each. This is not part of a route as it is a persistent part of the interface. I have run into a problem where only the view with the same name as the model gets the intended controller (i.e. the panel contents and not the tab headers).
I have a Chat model, object controller and array controller (deliberately simplified here):
App.Chat = DS.Model.extend({ });
App.ChatsController = Ember.ArrayController.extend({
needs: 'application',
content: Ember.computed.alias('controllers.application.currentChats'),
});
App.ChatController = Ember.ObjectController.extend({ });
The ArrayController needed the needs/content properties because the chats are loaded in the application controller. I used the currentChats name as other routes may load non-current chats.
App.ApplicationController = Ember.Controller.extend({
init: function(){
this.store.find('chat', {"current": true});
this.set('currentChats', this.store.all('chat'));
}
});
I have no difficulty rendering the chat contents with the appropriate controller (into the 'chat' template). However, the chat tabs are given the default ObjectController, and therefore can't fire actions.
<script type="text/x-handlebars" id="application">
<!--application template-->
{{outlet chats}}
</script>
<script type="text/x-handlebars" id="chats">
<div id="chats">
<ul id="chat-tabs">
{{#each}}
{{render 'chatTab' this}}
{{/each}}
</ul>
{{#each}}
{{render 'chat' this}}
{{/each}}
</div>
</script>
<script type="text/x-handlebars" id="chatTab">
<!--tab template-->
</script>
<script type="text/x-handlebars" id="chat">
<!--chat template-->
</script>
The application router is as follows:
App.ApplicationRoute = Ember.Route.extend({
model: function(){ },
renderTemplate: function(){
this.render('application', { });
this.render('chats', {
into: 'application',
outlet: 'chats',
controller: 'chats'
});
}
});
This seems to come solely down to naming of the templates. The template called 'chat' inherits the correct controller, but chatTab doesn't despite receiving a chat as the model. Is there any way to force the view to inherit the correct controller? Or am I going about this in an idiosyncratic way.
Many thanks for your help to this Ember novice.
Andrew
It goes solely off the name provided to the render. The easiest way is to just create the other controller and extend the chat controller.
App.ChatTabController = App.ChatController.extend();

Ember pre4 - nested routes

I'm trying to understand how to use nested routes.
My code:
App.Router.map(function() {
this.route("site", { path: "/" });
this.route("about", { path: "/about" });
this.resource("team", {path:'/team'}, function(){
this.resource('bob',{path:'/bob'});
});
});
And I'm trying to get to the Bob page with:
{{#linkTo 'bob'}}bob{{/linkTo}}
What am I missing?
jsbin
Thanks.
try instead
{{#linkTo 'team.bob'}}bob{{/linkTo}}
Between you can simplify your router map this way - you only need to specify the path if it's different from the route name.
App.Router.map(function() {
this.route("site", { path: "/" });
this.route("about");
this.resource("team", function(){
this.route('bob');
});
});
UPDATE
See a working example here
In summary, You need to provide an implementation of the renderTemplate function of TeamBobRoute where you explicitly specify where you want to render your template bob. Using the render option into you can override the default behaviour, rendering to the parent outlet, and pick which parent template to render to
App.TeamBobRoute = Ember.Route.extend({
renderTemplate:function(){
this.render('bob',{
into:'application',
});
}
});
<script type="text/x-handlebars" data-template-name="site-template">
This is the site template
{{#linkTo 'about'}}about{{/linkTo}}
{{#linkTo 'team'}}team{{/linkTo}}
</script>
<script type="text/x-handlebars" data-template-name="about">
This is the about page
</script>
<script type="text/x-handlebars" data-template-name="team">
This is the team page
{{#linkTo 'team.bob'}}bob{{/linkTo}}
</script>
<script type="text/x-handlebars" data-template-name="bob">
This is the bob page
</script>
<script type="text/x-handlebars">
This is the application template
{{outlet}}
</script>
FYI the render method supports the following options: into, outlet and controller as described below.
The name of the PostRoute, as defined by the router, is post.
By default, render will:
render the post template
with the post view (PostView) for event handling, if one exists
and the post controller (PostController), if one exists
into the main outlet of the application template
You can override this behavior:
App.PostRoute = App.Route.extend({
renderTemplate: function() {
this.render('myPost', { // the template to render
into: 'index', // the template to render into
outlet: 'detail', // the name of the outlet in that template
controller: 'blogPost' // the controller to use for the template
});
}
});
If you had a named template inside your application template then you would target it this way
App.TeamBobRoute = Ember.Route.extend({
renderTemplate:function(){
this.render('bob',{
into:'application',
outlet:'team-member',
});
}
});
<script type="text/x-handlebars">
This is the application template
{{outlet 'team-member'}}
{{outlet}}
</script>
You're missing the outlet in the team page. The template should look like this.
<script type="text/x-handlebars" data-template-name="team">
This is the team page
{{#linkTo 'bob'}}bob{{/linkTo}}
{{outlet}}
</script>
Each route is rendered into it's parent's template's outlet.
so when you go into "team", then "team" is rendered into the "application" outlet.
When you go to "bob", the "bob" template is rendered into the "team" outlet.
This can be overridden, but is the default behavior.
Also, each parent resources gives you two model/controller/view/template sets. So when you define:
this.resource('team',{path:'/team'});
You get the "team" template and the "team-index" template.
the "team" template is where stuff that is shared between child routes goes (this is why it needs to have the outlet) and the "team-index" template is where stuff that is specific to your "team index" would go.

Accessing Ember controller attributes from a controller

I have the following controller:
App.ShowController = Ember.Controller.expend({
buttonTitle: 'Create'
});
And the following template show.handlebars
<a href='#'>{{buttonTitle}}</a>
but the text is not rendering. Is there a special call to access the attribute?
Normally, when a view is displayed (via the Router), the context of the view is automatically set to the controller, so there should be nothing to do special.
Here is an example, where the MyApp.IndexController is automatically set as the context of the IndexView (and its template is the index template):
MyApp = Ember.Application.create({});
MyApp.Router = Ember.Router.extend();
MyApp.Router.map(function(match) {
match('/').to('index');
});
MyApp.IndexController = Ember.Controller.extend({
buttonTitle: "create"
});
The template:
<script type="text/x-handlebars" data-template-name="index">
{{buttonTitle}}
</script>
And you could try it on this JSFiddle.
N.B.: I'm using Ember v1.0.0-pre.2-239 here. There are some changes to do for upgrading this example to master

new ember.js routing: how to connect outlets?

I'm confused how to connect outlets with the new router approach.
index.html:
...
<script type="text/x-handlebars" data-template-name="application">
<h4>The application handelbar</h4>
{{! outlet 1}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<h4>The index handelbar</h4>
{{! outlet 2 and 3}}
{{outlet nav}}
{{outlet main}}
</script>
<script type="text/x-handlebars" data-template-name="main">
<h4>The main handelbar</h4>
</script>
<script type="text/x-handlebars" data-template-name="nav">
<h4>The nav handelbar</h4>
</script>
...
app.js:
...
App.Router.map(function(match) {
this.resource("index", { path: "/" });
this.route("test");
});
App.IndexController = Ember.Controller.extend({
});
App.IndexView = Ember.View.extend({
templateName: 'index'
});
...
This code renders outlet-1.
Questions:
Why is outlet-1 rendered? How are outlet-1 and "index" connected?
How can I connect outlet 2 and 3 to the same "index" site?
Thanks
miw
You need to specify this stuff in a route handler, using the renderTemplate method (or renderTemplates method, depending on your build).
What you're not seeing is that Ember is setting quite a few defaults for you already. In fact, the defaults set by Ember have allowed you to omit the entire route handler.
App.Router.map(function(match) {
this.resource("index", { path: "/" });
this.route("test");
});
App.IndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render();
/* this is the default, it will basically render the
default template, in this case 'index', into the
application template, into the main outlet (i.e. your
outlet 1), and set the controller to be IndexController.
*/
}
});
What you want is to render additional templates in the renderTemplate function, likeso:
renderTemplate: function() {
this.render("index");
// this renders the index template into the primary unnamed outlet.
this.render("navtemplate", {outlet: "nav"});
// this renders the navtemplate into the outlet named 'nav'.
this.render("main", {outlet: "main"});
// this renders the main template into the outlet named 'main'.
}
Hope this helps.
Ember automatically assumes / matches with IndexRoute, IndexController and IndexView. This is in the ember routing guide
To connect nested routes you can do it like this:
App.OtherRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('otherTemplate', {
into: 'index',
outlet: 'nav'
});
}
});
Here is a more in depth answer from another question.