new ember.js routing: how to connect outlets? - ember.js

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.

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: controller properties not available in partial views - what am I doing wrong?

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

Ember.js Rendering outlet as a modal window

I'm trying to render a modal. For that I've created a custom outlet using {{outlet modalOutlet}} My application template has two outlets, the default outlet and the modalOutlet. However when the modal template is rendered into {{outlet modalOutlet}}, my default {{outlet}} becomes empty.
How do I change it, so that the default {{outlet}} doesn't change, so I can actually render {{outlet modalOutlet}} as modal window, or as a sidebar as a part of a layout
I'm not sure if this is due to my code, or something about the renderTemplate() method that I'm missing. The jsFiddle with my code is here.
// Router
App.Router.map(function(){
this.route('contributors');
this.route('contributor', {path: '/contributors/:contributor_id'});
});
App.ContributorsRoute = Ember.Route.extend({
model: function() {
return App.Contributor.all();
},
});
App.ContributorRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('contributor', {
outlet: 'modalOutlet'
});
}
});
<script type="text/x-handlebars" data-template-name="application">
<nav>
{{#linkTo "index"}}Home{{/linkTo}}
{{#linkTo "contributors"}}Contributors{{/linkTo}}
</nav>
<div style='padding:5px;margin:5px;border:1px dotted red;'>
Default Outlet
{{outlet}}
</div>
<div style='padding:5px;margin:5px;border:1px dotted blue;'>
modalOutlet
{{outlet modalOutlet}}
</div>
</script>
You must render the contributors template as well, since the default outlet gets cleared when you transition to a sibling route.
App.ContributorRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('contributors');
this.render('contributor', {
outlet: 'modalOutlet'
});
}
});
You can avoid this, however, if you nest your routes like this:
App.Router.map(function(){
this.resource('contributors', function() {
this.route('show', {path: ':contributor_id'});
});
});
...and adjust the rest of your app to match the new structure. In this case, you need to specify the place the modalOutlet lies with the into option (in this case: 'application')
The issue is your routing structure is not nested, and once you nest your routes you will need to specify the route which contains the modal outlet.
What is happening is you render
Application -> Contributors
to show your list, but when you click a link you are now rendering
Application -> Contributor
and the Contributor template is removed.
If you nest your routes, like this:
Application -> Contributors -> Contributor
Then you will still have the Contributors template showing the list.
updated JSFiddle
//Router
App.Router.map(function(){
this.resource('contributors', function() {
this.resource('contributor', {path: '/:contributor_id'});
});
});
//Route
App.ContributorRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('contributor', {
into: 'application',
outlet: 'modalOutlet'
});
}
});

Ember.js Redirect to Template

I have a list of matches, and when I click one, I want to display the match. I know that I can do a Master-Detail style page, where when I click one, I can see the outlet somewhere on the same page, but that is not what I want.
I want it so that when I click on a link, it goes to an entirely new page for the match. I'm not really sure how to go about doing that.
Here is my route for #/matches (in coffeescript)
App.MatchesRoute = Ember.Route.extend(
model: ->
App.Match.find()
)
Here is my matches.handlebars
<div id="matches">
{{#each match in controller}}
{{#linkTo "match" match class="panel six columns"}}
Match between {{match.player.name}} and {{match.opponent.name}}
{{/linkTo}}
<br />
{{/each}}
</div>
// I know that if I have this outlet, it will render `match.handlebars`
// right here, but I want it to be it's own page.
// {{outlet}}
I've only been working with Ember for a few days, and all of the examples I've found use Master-Detail views.
Please let me know of any other information I can provide from my code.
<Edit date="March 11th 2013">
I've pushed a this repository in GitHub. This is a conceptual app that uses renderTemplate somewhat the way you're describing.
</Edit>
In your child route, use the renderTemplate hook in order to tell your application to render a specific template in a specific {{outlet}}. Example:
Source Fiddle
App.Router.map(function() {
this.resource('matches', { path: 'matches' }, function() {
this.route('match', { path: 'match/:match_id' });
});
});
App.MatchesRoute = Em.Route.extend({
model: function() {
return App.Match.find();
},
setupController: function(controller, model) {
model = App.Match.find();
controller.set('content', model);
},
renderTemplate: function() {
this.render('matches', {
into: 'application'
})
}
});
App.MatchesMatchRoute = Em.Route.extend({
model: function(params) {
return App.Match.find(params.match_id);
},
setupController: function(controller, model) {
controller.set('content', model);
},
renderTemplate: function() {
this.render('match', {
into: 'application'
})
}
});
This MatchesMatchRoute is setup to render its template (matches/match) into the application template. And since there is only one {{outelet}} this template (see handlebars below), we don't have to specify anything:
<script type="text/x-handlebars">
<h1>App</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="matches">
<h2>Matches</h2>
<ul>
{{#each match in controller}}
<li>
{{#linkTo matches.match match}}
{{match.title}}
{{/linkTo}}
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="match">
<h3>{{title}}</h3>
<p>{{description}}</p>
</script>
If you have a scenario with multiple outlets, you have to hame them, like in the handlebars below:
<script type="text/x-handlebars">
<h1>App</h1>
{{outlet main}}<br />
{{outlet nested}}
</script>
Then your routes will have to specify the outlet as well. Example:
Source Fiddle
[...route code...]
renderTemplate: function() {
this.render('content', {
into: 'application',
outlet: 'main'
});
this.render('buttons', {
into: 'application',
outlet: 'nested'
});
}
[...route code...]
You can cause a template to render into a different template's outlet by using the renderTemplate hook when defining the route (see the guide: http://emberjs.com/guides/routing/rendering-a-template/)
For your example it might look like this:
App.MatchRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({ into: 'matches' });
}
});