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'
});
}
});
Related
I have templates box/inbox.hbs and users/profile.hbs with {{outlet inbox}} in profile.hbs
In my profile.hbs I have {{#link-to 'box.inbox'}}this is a link{{/link-to}} which should render box/inbox.hbs into the outlet {{outlet inbox}} which I'm trying to do in my router.js with:
Router.BoxInboxRoute = Ember.Route.extend({
renderTemplate: function(){
this.render('box.inbox', {into: 'users.profile', outlet: 'inbox'});
}
});
but the link just redirects me to another page /box/inbox. How do I get it so that the inbox.hbs is rendered in the outlet on profile.hbs?
I think the link-to helper is the source of confusion. As far as I know, link-to will always change the URL and route, so you would never be able to achieve rendering the box.inbox route into a specific part of the users.profile template by clicking a link unless you use nested routes.
However, to achieve something like clicking on a link to show the inbox content, you could always load the box.inbox route hidden using the named outlet, then on click of a link, show the div containing the box.inbox contents. Here's an example.
UsersProfileRoute:
Router.UsersProfileRoute = Ember.Route.extend({
renderTemplate: function(){
this.render();
this.render('box.inbox', {into: 'users.profile', outlet: 'inbox'});
}
});
users/profile.hbs:
<a href='#' {{action 'showInboxAction'}}>click to see inbox</a>
<div {{bind-attr class=":inbox-style shouldShowInbox:displayed:hidden"}} >
{{outlet inbox}}
</div>
css:
.inbox-style.displayed {
display: block;
}
.inbox-style.hidden {
display: none;
}
and in the user profile controller have an action:
action:
{
showInboxAction: function()
{
this.set('shouldShowInbox', true);
}
}
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' });
}
});
I have just written extremly simple Ember app, built on top of the Rails app, working with Ember Data and displaying, creating and persisting just one entity type to the server. Everything with the latest tools (Ember v1.0.0-pre.4-134-gaafb5eb).
However, there is very strange problem I have encountered. My app has two views: entity list (index) and form for creating new entities. When I enter the index directly, everything displays OK. But when I go to the other view and then back to the list, the view is not rendered again. Where could be the problem?
I guess it might be caused by my (maybe incorrect) using new Ember router. So I'm pasting important (from my point of view) parts of the app here:
Router:
App.Router.map(function() {
this.resource('bands', function() {
this.route('new');
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('bands');
}
});
App.BandsRoute = Ember.Route.extend({
model: function() {
return App.Band.find();
}
});
App.BandsNewRoute = Ember.Route.extend({
renderTemplate : function(){
this.render('bands_new',{
into:'application'
});
}
});
Link back to list - which does not work:
App.BandsNewController = Em.ArrayController.extend({
cancel: function() {
this.transitionTo('bands');
}
});
Have a look at the whole app here: https://github.com/pavelsmolka/roommating
(It's hugely inspired by great https://github.com/dgeb/ember_data_example)
I don't believe it, but could it be bug in Ember itself?
I think your "render" call in your BandsNewRoute is messing things up.Try making things go more with Ember defaults. So I would refactor your app to do this:
(working fiddle: http://jsfiddle.net/andremalan/DVbUY/)
Instead of making your own render, all you need to do is create a "bands" template (it can be completely empty except for {{outlet}} if you want) and a "bands.index" template.
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="bands/index">
<h2>Bands Index</h2>
{{#linkTo bands.new}}New Band{{/linkTo}}
</script>
<script type="text/x-handlebars" data-template-name="bands">
<h1>Bands</h1>
<p>
{{#linkTo index}}Start Again{{/linkTo}}
{{#linkTo bands.new}}New Band{{/linkTo}}
</p>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="bands/new">
I'm in new band!
<a {{action "cancel"}}>Cancel</a>
</script>
Your routes also clean up really nicely this way:
App.Router.map(function() {
this.resource('bands', function() {
this.route('new');
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('bands');
}
});
App.BandsNewController = Ember.Controller.extend({
cancel: function() {
this.transitionTo('bands');
}
});
I hope that helps!
my index template has two outlets, one for header, another for content. the template rendered in the content changes depending on the content being viewed.
In the old router, this could be done by calling connectOutlet on different controllers who owned that template. I can't figure out how to do the same in the new router.
any suggestions?
With my research, I came to this:
Say you have a router defined like this:
App.Router.map(function(match) {
match('/').to('index');
});
ApplicationTemplate:
<script type="text/x-handlebars">
{{outlet header}}
{{outlet}}
</script>
IndexTemplate:
<script type="text/x-handlebars" data-template-name="index">
{{outlet dashboard}}
{{outlet spaces}}
</script>
Now, What we want is that when user goes to the index router, the page should:
Render index into main outlet and header into header outlet of application template.
render dashboard, spaces template into Index Template.
To achieve this, we write the following code in indexRoute
App.IndexRoute = Em.Route.extend({
renderTemplate: function(controller, model){
//Render header into header outlet
this.render('header',{
outlet:'header'
});
//Render index into main outlet. If you comment out
//this line, the code below fails
this.render('index');
//by using into, we can render into the index template
//Note: The controller is optional.if not specified,
//ember picks up controller for the given template.
this.render('dashboard',{
outlet:'dashboard',
into:'index',
controller:this.controllerFor('somethingElse', App.TestModel.find())
});
//controller is SpacesController
this.render('spaces',{
outlet:'spaces',
into:'index'
});
}
});
you can use the renderTemplates function in the router to render mulitple views to name outlets:
renderTemplates:function () {
this.render('todos_list', {
into:'todos', //template name
outlet: 'todos', //named outlet
controller: 'listController' //controller you want to use
});
this.render('todos_test', {
into:'todos',
outlet: 'test',
controller: 'testController'
});
},
setupControllers:function (controller, model) {
this.controllerFor('list').set('content', listmodel.find());
this.controllerFor('test').set('content', testmodel.find());
}
The setupControllerControllerFor function will allow you to assign what we set as 'context' in the previous router.
In your template, you would name outlets just as before:
{{outlet list}}
{{outlet test}}
Hope this helps :)
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.