How to use application model inside partial of route? - ember.js

I think this should be a really easy fix, i just cant figure it out.
I have this model in my ApplicationRoute:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return {
sideNav: data.side,
breadcrumbs: Util.breadcrumbs()
}
}
});
application template:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
overview template:
<script type="text/x-handlebars" data-template-name="overview">
{{partial side}}
</script>
_side partial:
<script type="text/x-handlebars" id="_side">
{{#each model.side}}
<i {{bindAttr class="iconClass"}}></i><p>{{label}}</p>
{{/each}}
</script>
When I load my overview route, the stuff in there loads, but the application model is not applied to the side partial like I would like it to be... there may be a better way to do this. Thanks!

It looks like you have two problems:
You are binding to side instead of sideNav inside your partial.
You're trying to access the model of ApplicationRoute inside of the overview template (which presumably has its own route and controller)
Partials don't change context, so they don't add any complexity on their own. For instance, if we restrict ourselves to the application template, the following will work:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return {
foo: 'bar'
};
}
});
<script type="text/x-handlebars" data-template-name="application">
{{partial partial}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="_partial">
{{foo}}
</script>
However, if you want to get at the application model inside of a nested route, you'll need to ask for it. For example, inside of the (default) index route, you could add the following:
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.modelFor('application');
}
});
<script type="text/x-handlebars" data-template-name="index">
{{partial partial}}
</script>
You can see both examples in this jsfiddle.

Related

Template doesn't render into named outlet

I'm trying to create several pages that will use the same menu so that I can save the state of the menu easily and avoid code duplication. My problem is that the menu template doesn't render in the menu outlet of the pages.
Here is the code :
App.Router.map(function() {
// put your routes here
this.resource('profil', function() {
this.route('news');
this.route('menu');
});
});
App.MenuRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({
into: 'profil/news',
outlet: 'menu',
});
}
});
And in index.html I have :
<script type="text/x-handlebars" id="profil/news">
...
{{outlet}}
{{outlet menu}}
</script>
<script type="text/x-handlebars" id="menu">
...
</script>
The template 'profil/news' is rendered but the template 'menu' is not rendered at all : it should be rendered inside 'profil/news' when I visit profil/news . Any idea ?
Thank you
The problem was that I needed to use :
<script type="text/x-handlebars" id="profil/news">
...
{{outlet}}
{{render 'profil/menu'}}
</script>
And to change the name of the handlebar to match the routing:
<script type="text/x-handlebars" id="profil/menu">
Moreover, the statement beginning with App.Menuroute is unnecessary and be removed.
Now everything is working as expected.

How to render template other than application.hbs in EmberJS?

In EmberJS, the main template file is the application.hbs. Any template rendered from the routes goes the the {{outlet}} of this main template file.
Now, I have another main template file, say print.hbs wherein the template design is very different from the application.hbs. How do I do this?
In the router file, I have:
App.Router.map(function() {
this.resource('print', function() {
this.route('display1');
this.route('display2');
});
this.route('dashboard', {path: '/'});
this.route('anything');
});
The routes dashboard and anything uses the application.hbs.
What should I do to use print.hbs on the print route? Please help.
You can't easily change application template. Ember doesn't listen on templateName property changes and responds poorly when you try to re-render the template yourself.
A nice way to do this would be to use different partials within your application template, based on whether you are in the 'screen' or 'print' mode.
<script type="text/x-handlebars">
{{#if isPrint}}
{{partial "application-print"}}
{{else}}
{{partial "application-normal"}}
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="application-normal">
<div id="app-normal">
<h2>Normal template</h2>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="application-print">
<div id="app-print">
<h2>Print template</h2>
{{outlet}}
</div>
</script>
App.ApplicationController = Ember.Controller.extend({
isPrint: false,
currentPathChange: function () {
var currentPath = this.get("currentPath");
var isPrint = currentPath ? currentPath.indexOf("print") === 0 : false;
this.set("isPrint", isPrint);
}.observes('currentPath').on("init")
});
This JSBin will demonstrate why this, unfortunately, doesn't work either. According to this bug report, Ember's handlebars gets confused when there are multiple outlet directives in the same page, even if they are in different #if scopes.
Until this gets fixed, I propose the following, slightly modified, solution.
Application template is empty. One template each for normal and print section.
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="normal">
<div id="app-normal">
<h2>Normal template</h2>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="print">
<div id="app-print">
<h2>Print template</h2>
{{outlet}}
</div>
</script>
In router, everything goes into normal and print resources. Normal resources are placed at /, so that all the links remain the same. No need for special coding in ApplicationController.
App.Router.map(function() {
this.resource("print", function () {
this.route("a");
this.route("b");
});
this.resource("normal", {path: "/"}, function () {
this.route("a");
this.route("b");
});
});
Working jsbin here.

Is "basic" a reserved route name in Ember?

I have a route in an Ember app called "basic" corresponding to the name of an API endpoint.
This route doesn't work - links to it don't render its template.
Here's a JSBin demonstrating the failure: http://emberjs.jsbin.com/hisoxadi/1
JS:
App = Ember.Application.create();
App.Router.map(function() {
this.route('basic');
this.route('test');
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
Templates:
<script type="text/x-handlebars">
<h2> Welcome to Ember.js</h2>
{{#link-to 'index'}}Index{{/link-to}}
{{#link-to 'basic'}}Basic Route{{/link-to}}
{{#link-to 'test'}}Test Route{{/link-to}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="basic">
basic route is here
</script>
<script type="text/x-handlebars" data-template-name="test">
test route is here
</script>
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each item in model}}
<li>{{item}}</li>
{{/each}}
</ul>
</script>
To expand on your comment a bit, basic is indeed a reserved word. Specifically, it's a reserved word for the resolver. You can see the source here.
useRouterNaming: function(parsedName) {
parsedName.name = parsedName.name.replace(/\./g, '_');
if (parsedName.name === 'basic') {
parsedName.name = '';
}
},
And because of the way that Ember.js sometimes looks up routes and controllers in the container, it's a fair bet to say that there's no way around this without major code changes. There should probably be a documentation issue filed for this.
EDIT: I created an issue for this here.

basic nested routes in emberjs

I have this router
App.Router.map(function() {
this.resource('about', function() {
this.route('new');
});
});
and
<script type="text/x-handlebars" data-template-name="about/new">
<h1>About/New Page</h1>
</script>
However when I go to about/new route, I see the about template only. So, how do I render the template of a nested route?
Here's a jsfiddle: http://jsfiddle.net/C4gSE/
The outlet helper is missing in about template.
It should be
<script type="text/x-handlebars" data-template-name="about">
<h2>About</h2>
{{outlet}}
</script>
You need change the data-template-name from about to about/index.
And the linkTo from about to about.index
Like this sample

Break up my application.handlebars into separate templates using Ember.js and Ember.Router

I'm building a front-end (on top of Ruby on Rails) using ember.js and the ember-rails gem.
My (ember) application consists of Models, Views, Controllers and an application.handlebars template which describes my UI.
Whats the best practice to break up this application.handlebars file so that I can manage the UI? For example, I'd like to have Navigation at the top of the page.
I've tried using the Ember.Router, a separate navigation.handlebars (with NavigationView and NavigationController) the {{outlet}} helper to no avail. Here's what the Router looks like:
App.Router = Ember.Router.extend(
enableLogging: true
root: Ember.Route.extend(
index:
route: '/'
connectOutlets: (router, context) =>
router.get('applicationController').connectOutlet('navigation')
)
and the application.handlebars
<h1>Lots of HTML that I want to break up</h1>
{{outlet}}
Let me know if you need more info...thanks.
As per my Understanding, Let's suppose you want 3 sections(can be any number) Header, Content & Footer, You can do something as follows
<script type="text/x-handlebars" data-template-name="application">
{{view MyApp.HeaderView}}
{{#view MyApp.ContentView}}
{{outlet}}
{{/view}}
{{view MyApp.FooterView}}
</script>
<script type="text/x-handlebars" data-template-name="app-header">
All your Header related HTML
</script>
<script type="text/x-handlebars" data-template-name="app-content">
HTML related to content
{{yield}} //this will be explained at the end
More HTML if you want
</script>
<script type="text/x-handlebars" data-template-name="app-footer">
HTML related to footer
</script>
MyApp.HeaderView = Ember.View.extend({
templateName: 'app-header'
})
MyApp.ContentView = Ember.View.extend({
templateName: 'app-content'
})
MyApp.FooterView = Ember.View.extend({
templateName: 'app-footer'
})
MyApp.ApplicationView = Ember.View.extend({
templateName: 'application'
})
explaining {{yield}} In a nutshell, whatever is between in the block helper of a given view goes in there, In the above example for the MyApp.ContentView, the {{outlet}} defined in the {{#view MyApp.ContentView}} handlebars gets inserted at the {{yield}}
On the similar lines let me show the difference between layoutName property & templateName property,
App.someView = Ember.View.extend({
tagName: 'a',
templateName: 'a-template',
layoutName: 'a-container'
})
<script type="text/x-handlebars" data-template-name="a-template">
Hi There
</script>
<script type="text/x-handlebars" data-template-name="a-container">
<span class="container">
{{yield}}
</span>
</script>
Will result in following HTML
<a class="ember-view" id="ember235">
<span class="container ember-view" id="ember234">
Hi There
</span>
</a>
Use these concepts to split the application handlebars in your case it would be something like
{{view App.NavigationView}}
{{outlet}}
Update as per latest ember
The new ember supports partials similar to rails, now we can modify the above to use {{partial}} as follows:
{{partial "header"}}
{{outlet}}
{{partial "footer"}}
Ember when encountered this template will look for the template whose name is _header(similar to rails) and inserts the template(same goes for footer)
And If want to associate a controller we can use {{render}} helper
{{render "sidebar"}}
inserts the template whose name is sidebar at specified location in handlebars besides it also associates App.SidebarController to it,
Note: we cannot use {{render 'sidebar'}} more than once in same handlebars file.
But again if you want to use a widget like view multiple places in a given page then use {{view}} helper
For this problem, what you need to do is think about what views change and where that changes happen. If for example you have a navigation section and a main section, then think about how each of these sections change with the state of your application. Be sure to only create an {{outlet}} for dynamic content, otherwise things will get messy and the application will be slower. Then setup your templates and your router similar to the example below.
Templates:
<script type="text/x-handlebars" data-template-name="application">
<!--Your application template goes here-->
{{outlet navigation}}
{{outlet body}}
</script>
<script type="text/x-handlebars" data-template-name="navigation">
<!--Your navigation template goes here-->
</script>
<script type="text/x-handlebars" data-template-name="main-one">
<!--Your mainOne template goes here-->
</script>
<script type="text/x-handlebars" data-template-name="main-two">
<!--Your mainTwo template goes here-->
</script>
Note: You can have {{outlet}} in any of your view templates to change in more sub-states
Javascript:
window.App = Em.Application.create({
ApplicationView: Em.View.extend({
templateName: "application"
}),
ApplicationController: Em.Controller.extend({
}),
NavView: Em.View.extend({
templateName: "navigation"
}),
NavController: Em.Controller.extend({
}),
MainOneView: Em.View.extend({
templateName: "main-one"
}),
MainOneController: Em.Controller.extend({
}),
MainTwoView: Em.View.extend({
templateName: "main-two"
}),
MainTwoController: Em.Controller.extend({
})
Router: Em.Router.extend({
root: Em.Route.extend({
index: Em.Route.extend({
route: '/',
connectOutlets: function(router,context) {
router.get("applicationController").connectOutlet("navigation","nav");
router.get("applicationController").connectOutlet("body","mainOne");
}
}),
otherState: Em.Route.extend({
route: '/other-state',
connectOutlets: function(router,context) {
router.get("applicationController").connectOutlet("navigation","nav");
router.get("applicationController").connectOutlet("body","mainTwo");
}
}),
})
})
});
App.initialize();
Note: The applicationController must extend Controller and not ObjectController or ArrayController