Assume I have routemap like this:
App.Router.map ->
#resource 'posts', ->
#resource 'post', {path: '/:post_id'}
So I have list of posts on /posts route and a single post on posts/2. By default posts template will be rendered to {{outlet}} of a parent template (application) which is OK and post template to {{outlet}} of posts template which is not what I want. I want to replace list of posts with a single post.
So I came with that solution to set desired template to render to it's {{outlet}}:
App.PostRoute = Em.Route.extend
renderTemplate: ->
this.render(into: 'application')
But as a result I have list of posts and a single post rendered into one {{outlet}}. I can emtpy {{outlet}} each new route but then it breaks back button, cuz Ember wont render previous template again, assuming that is already rendered.
So questions is how to use single {{outlet}} for different routes and don't break ember's logic. Of course I can avoid nested routes but they a really usefull when you need semantic links like this post/2/comment/12/edit.
If your templates are not nested, you shouldn't nest your routes, or you'll end up fighting Ember. Acting as Ember's state manager, routes describe your app structure. Nesting should follow your UI, not how you want your URLs to look like.
If your concern is URLs, you can just modify the path to fit your needs:
App.Router.map ->
#resource 'posts'
#resource 'post', { path: 'posts/:post_id' }
The approach I have used for handling nested and non nested templates even when my routes are nested is to use named outlets. Ember makes it really easy.
I have a top level menu outlet that always holds a menu relevant to the route being visited.
Example Application Template:
<div id="nav">
{{partial "nav"}}
</div>
<div id="menu">
{{outlet "menu"}}
</div>
<div id="main">
{{outlet}}
</div>
{{outlet "modal"}}
I have two named outlets, "menu" and "modal", which are used to hold content which is not nested but used by nested routes or any route to be precise. Any route can render a modal in response to actions into the global "modal" outlet, and similarly any route can render a menu into the "menu" outlet.
My examples use coffeescript.
Router:
App.Router.map ->
#resource "posts", ->
#route "create"
#resource "post", {path: ':id'}, ->
#resource "comments", {path: ':id'}, ->
#route "create"
#resource "comment", {path: ':id'}, ->
Routes which render a menu into a named outlet that is not nested:
App.PostsIndexRoute = Em.Route.extend
renderTemplate: (controller, model)->
# default render
#_super arguments...
# render posts menu into the application menu outlet
#render "posts.menu",
outlet: "menu"
into: "application"
App.CommentsIndexRoute = Em.Route.extend
renderTemplate: (controller, model)->
# default render
#_super arguments...
# render comments menu into the application menu outlet
#render "comments.menu",
outlet: "menu"
into: "application"
You don't have to do the default nested rendering as above, you could just define a route type that always renders into a named outlet such as "content" (just don't call it "main" because Ember uses that).
App.ContentRoute = Em.Route.extend
renderTemplate: (controller, model)->
#render outlet: "content", into: "application"
And then subclass from it for any routes that should always render into the application 'content' outlet:
App.PostsIndexRoute = App.ContentRoute.extend()
App.CommentsIndexRoute = App.ContentRoute.extend()
But it would be better to do it with a mixin so you can easily include whatever concerns you wish into any route (eg authenticated route concerns).
App.RenderIntoContent = Em.Mixin.create
renderTemplate: (controller, model)->
#render outlet: "content", into: "application"
App.PostsIndexRoute = Em.Route.extend App.RenderIntoContent,
...
App.CommentsIndexRoute = Em.Route.extend App.RenderIntoContent,
...
Related
Given a list of routes:
Router.map ->
#route 'home', path: '/'
#route 'sign-in'
#route 'about'
How can I dynamically render this information into my application template (main layout)?
<div id="container" class="{{routeNameGoesHere}}">
{{outlet}}
</div>
For example, fully rendered:
<div id="container" class="sign-in">
<h1>Sign In Page</h1>
</div>
Application Controllers automatically receive the properties currentPath and currentRouteName from the router. So you can use those directly in your application template.
Based on #Grapho's answer, I found that I needed to create a computed property on my application controller in order to convert the dot-separated route names with dashes.
import Ember from 'ember';
export default Ember.Controller.extend({
hyphenatedCurrentRouteName: Ember.computed('currentRouteName', function(){
return this.get('currentRouteName').split('.').join('-')
}
});
Now in my template, I can use {{hyphenatedCurrentRouteName}}.
I'm attempting to create an Ember application with nested views. I (basically) want three vertical columns. The left-most containing a list of Environments. Clicking an environment will populate the second (centre) column with the Job objects associated with the environment, and then when clicking on a Job in second column it populates the third column with various details based on the Job.
The data is coming from a Rails api via ActiveModel serializers, embedding IDs as required.
So far, I have the following routes defined:
Skills.Router.map ()->
this.resource 'environments', ->
this.resource 'environment', {path: ":environment_id"}, ->
this.resource 'jobs'
Skills.IndexRoute = Ember.Route.extend
redirect: ->
this.transitionTo 'environments'
Skills.EnvironmentsRoute = Ember.Route.extend
setupController: (controller) ->
controller.set('model', this.store.find('environment'))
My handlebars application template:
<div class="container">
<div class="row">
{{outlet }}
</div>
</div>
The Environments template
<div id="environments" class="col-sm-4">
{{#each }}
{{view Skills.EnvironmentView }}
{{/each}}
</div>
<div class="col-sm-8">
{{outlet}}
</div>
Environments View:
Skills.EnvironmentView = Ember.View.extend
templateName: 'environment'
click: ->
# should I be trying to update the Jobs controller here?
Environments controller:
Skills.EnvironmentsController = Ember.ArrayController
Models:
Skills.Environment = DS.Model.extend
title: DS.attr('string')
description: DS.attr('string')
jobs: DS.hasMany('job')
Skills.Job = DS.Model.extend
title: DS.attr('string')
environment: DS.belongsTo('environment')
This will lookup and display the Environments in the first column. How do I get the Jobs controller populated with the selected Environments Jobs from the association?
Edit: Binding seems like what I'm after, but I'm unsure as to how to bind the model/content property of one controller to a property from another?
What you should do is populate the list of environments in the environments template
(not the environments.index template). When an environment is selected from this list it should transition to the environment.show route. In the environment resource route it should show the list of jobs for that environment. This environment template should be rendered into the environments template. Then choosing a job will transition to the job.show route underneath the jobs resource.
The key is that the top level resource route will be retained and the subsequent child routes will be rendered into it, preserving the resource lists while rendering the child details.
Skills.Router.map ()->
this.resource 'environments', ->
this.route 'index'
this.resource 'environment', {path: ":environment_id"}, ->
this.route 'index'
this.route 'show'
this.resource 'jobs'
this.route 'index'
this.resource 'job'
this.route 'show'
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'
});
}
});
I am having trouble with ember.js. Using the following routing setup I can not get the entries/new route to work. The index works fine but the entries/new template just refuses to render. I think it is where I am trying to render the view inside renderTemplate but I'm not sure what I'm doing incorrect. Your help would be much appreciated.
Journal.Router.map ->
#resource 'entries', {path: '/' }, ->
#route 'new'
return
return
Journal.EntriesNewRoute = Ember.Route.extend
renderTempalte: ->
#render 'entriesNew', {
into: 'application'
}
setupController: (controller) ->
controller.set 'heading', 'new entry'
return
Journal.EntriesNewView = Ember.View.extend
className: ['entries-new']
templateName: 'entries/new'
Journal.EntriesNewController = Ember.Controller.extend
heading: "New Journal Entry"
createEntry: ->
title = #get 'newTitle'
content = #get 'newContent'
if !title.trim() and !content.trim() then return null
Journal.Entry.createRecord
title: title
content: content
#get('store').commit()
return
And the entries/new template
{{ heading }}
{{view Ember.TextField id="entry-title" placeholder="Enter a title" valueBinding="newTitle"}}
{{view Ember.TextArea id="entry-content" placeholder="What do you have to say?" valueBinding="newContent"}}
<button {{action "createEntry"}} class="save">Save</button>
In your route, the 'into' should target the route that has your {{outlet}}
renderTempalate: ->
#render 'entriesNew', {
into: 'entries'
}
Though the renderTemplate hook's default action is to render into it's resources outlet, so as long as your Entries template has an {{outlet}} in it and your obeying the strict naming conventions, you don't need to define that hook at all.
If that's not the issue maybe post your Entries template
In my Ember app I have a couple different menus or nabbers that I need to display depending on the section of the app the user is in. What is the recommended way of doing this? What I was attempting to do was have view in my template:
{{view App.NavbarView controllerBinding="App.CurrentNavbarController.nav"}}
And in my view I'm checking the path:
App.NavbarView = Ember.View.extend({
templateName: function() {
path = App.getPath('router.currentState.path');
//change navbar
}
});
That's where I'm stuck. How do I dynamically switch the views?
Another way would be to use the router to to connect some outlets to a Nav view.
In your application.handlebars put a named outlet:
{{outlet nav}}
In your router, connect the outlet when you enter a route:
userRoute:
connectOutlets: (router, model)->
# normal stuff here
router.get('applicationController').connectOutlet('navbar', 'userNav', model)
exit: (router)->
router.get('applicationController').disconnectOutlet 'navbar'
subRoute:
# still has the navbar outlet connected in a subroute
One way would be to use the router to set it up.
someRoute:
connectOutlets: (router, model)->
# ... normal stuff here
App.set 'section', 'theNameOfThisSection'
exit: (router)->
App.set 'section', ''
subRoute:
# ... in the sub route, App.section will still be set