Ember JS super basic routing, using just static templates - ember.js

I have been trying to wrap my head around how Ember routing works on a very basic level and need a little kick in the right direction. I am providing the code I have written so far, hopefully you can follow along and see what is going on
I would like to see a page hierarchy like so:
index
--Red
----Light
----Medium
----Dark
--Green
----Light
----Medium
----Dark
--Blue
----Light
----Medium
----Dark
The "medium" colors are actually going to be the index for each nested route, So I think I need router.js set up like so:
Colors.Router.map(function(){
this.resource('red', { path: '/red' }, function(){
this.route('light-red');
this.route('dark-red');
});
this.resource('green', { path: '/green'}, function(){
this.route('light-green');
this.route('dark-green');
});
this.resource('blue', { path: '/blue'}, function(){
this.route('light-blue');
this.route('dark-blue');
});
});
For now, I just want to serve up static html templates (with just a little bit of #link-to handlebars for navigation)... just so I can see a working prototype.
The main template is here
<div id="emberColors">
<header id="mainHeader">
<h1>Ember Colors</h1>
<p>Pick a color</p>
<nav class="navi main">
<ul>
<li>{{#link-to "red" activeClass="active"}}Red{{/link-to}}</li>
<li>{{#link-to "blue" activeClass="active"}}Blue{{/link-to}}</li>
<li>{{#link-to "green" activeClass="active"}}Green{{/link-to}}</li>
</ul>
</nav>
</header>
{{outlet}}
<footer id="footer">
<p>Ok Buh-bye</p>
</footer>
I have a template for each of the three colors, each is set up like this:
<section id="blue">
<header>
<h1>The Blue</h1>
<p>Blue is cool.</p>
<nav class="navi sub">
<ul>
<li>{{#link-to "blue.light-blue"}}Light{{/link-to}}</li>
<li>{{#link-to "blue"}}Base{{/link-to}}</li>
<li>{{#link-to "blue.dark-blue}}Dark{{/link-to}}</li>
</ul>
</nav>
</header>
{{outlet}}
</section>
And I broke up each "shade" into multiple templates
<div class="color-lt">
<p>Light Blue</p>
</div>
Note: I also understand that I can reuse templates and load different model data into them, rather than hard coding lots of little templates... but I am just trying to focus on learning the routing at the moment.
Because these are just basic templates, I assume (based on what I have read) Ember auto-generates a lot of the MVC and I don't need to define any specific controllers, models, or routes (except for perhaps the IndexRoutes for the three main color landing pages).
I understand that the naming convention for the templates will help ember do most of the work.
However it is the naming conventions that are confusing me.
in router.js it seems simple enough to define the routes by how I want it to appear in the URL
in handlebars {{#link-to}} though, it looks like I need to reference the URL (or template?) name with a . separation (include dashes or no)?
and when defining an index route it is structured with PascalCase?
I need some help with figuring out how to properly name/declare these routes so that Ember can render them correctly.
for instance: because of the nested nature of these pages... do I need to store my templates in a nested directory file structure as well? or is that being done artificially by ember routing?
Is there a general MVC Routing methodology that I am supposed to understand before I can understand Ember Routing specifically?
Sorry, I have a lot of questions about this... It seems like it is supposed to be really simple, but I don't know why I am not getting it. Any help will be greatly appreciated.
EDIT: I will also let you know that, as I have it written out currently, Ember Inspector is not throwing any errors that I can see. So I am convinced, that so far I am on the right track, but I am just not providing anything for the router to resolve...

Related

Ember.js: Route-dependent Menu

I've got a Template which defines an <div id="sidemenu"><ul>...</ul></div>.It should has some <li>-Elements which represents general available-Menu-Items. On Sub-routes I want to add some additional menu-entries which only make sense on that routes. But since the div where to put the li's into is defined in the parent-route-template, I don't know how I should implement this.
My first theoretical approach was to call a controller function which returns the necessary li's. I could override this function in each sub-controller but I don't know if this is good practice.
However I want to do it in a ember-vanilla way if possible (someone told me to use a plugin called "wormhole" or something)
I would make the dependent portions of the menu a named outlet, then render into the outlet from each route's renderTemplate hook.
{{! components/sidebar/template.hbs }}
<div id="sidemenu">
<ul>
<li>Generally available menu item 1</li>
{{outlet name='nav'}}
^^^^^^^^^^^^^^^^^^^^^
</ul>
</div>
// thing1/route.js
renderTemplate() {
this.render('thing1-nav-template', {into: 'nav'});
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
this._super(...arguments);
}
You could use {{ember-wormhole}} I suppose--I don't know that module. It certainly does not fall into the category of "ember vanilla" if that's one of your criteria. Essentially, it would allow you to write the thing1-nav-template contents directly in your route template instead of another template. I have no idea how stable it is, so personally I'd do it the old-fashioned way using outlets; that's what they're there for.
You can use https://github.com/yapplabs/ember-wormhole to render content from one template into an HTML element identified by an ID.
Menu Template:
<ul>
<li>General stuff</li>
</ul>
<ul id='foobar' />
Subroute Template:
{{#ember-wormhole to="foobar"}}
<li>Subroute stuff</li>
{{/ember-wormhole}}

Nested routes and outlets within outlets

This could be a rather trivial question, but as an Ember beginner, I am struggling with it a bit.
The site I'm building has a parent navigation, but some of the templates inside its outlet may have their own navigation and their own outlet (at least that's what I'm intending). In other words, I might have something like (skipping a lot of HTML):
<script type="text/x-handlebars" data-template-name="application">
<nav>
<ul class="nav">
<li>{{#link-to 'index'}}Home{{/link-to}}</li>
<li>{{#link-to 'testing'}}Testing{{/link-to}}</li>
</ul>
</nav>
<div>{{outlet}}{/div>
</script>
<script type="text/x-handlebars" data-template-name="testing">
<ul class="nav">
<li>{{#link-to 'testing.encoding'}}Encoding{{/link-to}}</li>
<li>{{#link-to 'testing.user'}}User Admin{{/link-to}}</li>
</ul>
<div>{{outlet}}{/div>
</script>
<script type="text/x-handlebars" data-template-name="testing/encoding">
<h3>Encoding</h3>
</script>
<script type="text/x-handlebars" data-template-name="testing/user">
<h3>User Admin</h3>
</script>
The application template wires great. Click on "Home", you get the index template; click on "Testing", you get the testing template. Super. What I want is for when they click "Encoding", the route is then /testing/encoding and the testing/encoding template is rendered in the outlet inside of testing. My Router looks like this:
App.Router.map(function() {
this.route('testing', function() {
this.route('encoding');
this.route('users');
});
});
However, the page won't load at all, giving me: Uncaught Error: There is no route named encoding.index. I suspect I named my templates poorly, or configured my Router incorrectly, or perhaps need to name the outlet in the testing template... and I've attacked those potential issues, but have yet to come up with a resolution.
Ideas? This is probably a typical pattern and likely has a clean, easy solution with minimal javascript. I'm just not seeing it, I suppose.
You want your router to look like this.
App.Router.map(function() {
this.resource('testing', function() {
this.route('encoding');
this.route('users');
});
});
You can't nest a route under a route. What you get with the resource is the leaf routes of index, loading and error.
There is not route encoding.index in this router.
The routing guide here is really helpful.
Cheers
For one you didn't name your route correctly (it should be this.route('encoding'); instead of this.route('encode');. But the bigger issue is that Ember doesn't allow for nested routes, only nested resources (and routes nested in resources).
The Ember docs say: "You cannot nest routes, but you can nest resources."

Ember.Component (block form): more than one outlet {{yield}}

I see that ember has a very nice mechanism for wrapping content in a component using the {{yield}} mechanism documented here.
So, to use the example in the documentation, I can have a blog-post component template defined like so:
<script type="text/x-handlebars" id="components/blog-post">
<h1>{{title}}</h1>
<div class="body">{{yield}}</div>
</script>
I can then embed blog-post into any other template using the form:
{{#blog-post title=title}}
<p class="author">by {{author}}</p>
{{body}}
{{/blog-post}}
My question is, can I specify two different {{yield}} outlets in the components template?
Something like this is possible via Named Outlets in Ember.Route#renderTemplate like so:
Handlebars:
<div class="toolbar">{{outlet toolbar}}</div>
<div class="sidebar">{{outlet sidebar}}</div>
JavaScript:
App.PostsRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({ outlet: 'sidebar' });
}
});
I'm not sure I can take this path for a component which will not know what route's template would be rendering it.
EDIT 1:
For the sake of clarity, I'm trying to implement the Android Swipe for Action Pattern as an Ember component.
So, I'd like users of this component to be able to specify two different templates:
A template for the normal list item, and
A template for the actions that are revealed when a swipe on (1) is detected.
I want to make this into a component, because quite a lot of javascript goes into handling the touch(start/move/end) events, while still managing smooth touch based scrolling of the list. Users would supply the two templates and this component would manage handling of touch events and necessary animations.
I've managed to get the component working in the block form, where the block's contents are treated like (1). The second template (2) is specified through a parameter (actionPartial below) which is the name of a partial template for the actions:
Component Handlebars Template: sfa-item.handlebars
<div {{bind-attr class=":sfa-item-actions shouldRevealActions:show" }}>
{{partial actionPartial}}
</div>
<div {{bind-attr class=":sfa-item-details isDragging:dragging shouldRevealActions:moveout"}}>
{{yield}}
</div>
Calling Handlebars Template:
{{#each response in controller}}
<div class="list-group-item sf-mr-item">
{{#sfa-item actionPartial="mr-item-action"}}
<h5>{{response.name}}</h5>
{{/sfa-item}}
</div>
{{/each}}
Where the mr-item-action handlebars is defined like so:
mr-item-action.handlebars:
<div class="sf-mr-item-action">
<button class="btn btn-lg btn-primary" {{action 'sfaClickedAction'}}>Edit</button>
<button class="btn btn-lg btn-primary">Delete</button>
</div>
Problem is, actions from the user supplied partial, sfaClickedAction above, are not bubbled up from the component. A fact which is mentioned in the docs in para 4.
So, now I do not know how a user could capture actions that he defined in the supplied actions template. A component cannot catch those actions because it doesn't know about them either.
EDIT 2
I sprung a follow up question here
This blog post describes the most elegant solution for Ember 1.10+: https://coderwall.com/p/qkk2zq/components-with-structured-markup-in-ember-js-v1-10
In your component you pass yield names into {{yield}}s:
<header>
{{yield "header"}}
</header>
<div class="body">
{{yield "body"}}
</div>
<footer>
{{yield "footer"}}
</footer>
When you invoke your component, you accept the yield name as a block param... and use an esleif chain!
{{#my-comp as |section|}}
{{#if (eq section "header")}}
My header
{{else if (eq section "body")}}
My body
{{else if (eq section "footer")}}
My footer
{{/if}}
{{/my-comp}}
PS eq is a subexpression helper from the must-have ember-truth-helpers addon.
PPS Relevant RFC: proposal, discussion.
Since it is not possible to have two {{yield}} helpers within one component (how would the component know where one {{yield}}'s markup stops and the next one begins?) you may be able to approach this problem from a different direction.
Consider the pattern of nested components. Browsers do this already with great success. Take, for example, the <ul> and <li> components. A <ul> wants to take many bits of markup and render each one like a member of a list. In order to accomplish this, it forces you to separate your itemized markup into <li> tags. There are many other examples of this. <table>, <tbody>, <tr>, <td> is another good case.
I think you may have stumbled upon a case where you can implement this pattern. For example:
{{#sfa-item}}
{{#first-thing}}
... some markup
{{/first-thing}}
{{#second-thing}}
... some other markup
{{/second-thing}}
{{/sfa-item}}
Obviously first-thing and second-thing are terrible names for your specialized components that represent the things you'd want to wrap with your first and second templates. You get the idea.
Do be careful since the nested components won't have access to properties within the outer component. You'll have to bind values with both outer and inner components if they are needed in both.

Understand routing in Ember.js

I'm really struggling to understand the routing concept in Ember but is more complicate than what it seem. From the doc. you can see you have different route whenever there is different url or path and if you have different path in the same url, easy you just need to create a nested template.
But what about when you have 3 different path in one url?
And what's the difference from this.resource and this.route?
Since live example are always better than pure theory, here my app.
In index or '/' I should render "list template", "new template" and when user click on a list link, the "note template" is render instead "new template".
My router:
Notes.Router.map(function () {
this.resource('index', { path: '/' }, function (){
this.resource('list', {path: ':note_title'});
this.resource('new', {path: '/'});
this.resource('note', { path: ':note_id' });
});
});
My template:
<script type="text/x-handlebars" data-template-name="index">
<div class="wrap">
<div class="bar">
{{input type="text" class="search" placeholder="Where is my bookmark??" value=search action="query"}}
<div class="bar-buttons">
<button {{action "addNote"}}> NEW </button>
</div>
</div>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="list">
<aside>
<h4 class="all-notes">All Notes {{length}}</h4>
{{#each item in model}}
<li>
{{#link-to 'note' item}} {{item.title}} {{/link-to}}
</li>
{{/each}}
</aside>
</script>
<script type="text/x-handlebars" data-template-name="new">
<section>
<div class="note">
{{input type="text" placeholder="Title" value=newTitle action="createNote"}}
<div class="error" id="error" style="display:none"> Fill in the Title! </div>
{{input type="text" placeholder="What you need to remember?" value=newBody action="createNote"}}
{{input type="text" placeholder="Url:" value=newUrl action="createNote"}}
</div>
</section>
</script>
<script type="text/x-handlebars" data-template-name="note">
<section>
<div class="note">
{{#if isEditing}}
<h2 class="note-title input-title"> {{edit-input-note value=title focus-out="modified" insert-newline="modified"}} </h2>
<p class="input-body"> {{edit-area-note value=body focus-out="modified" insert-newline="modified"}} </p>
{{edit-input-note value=url focus-out="modified" insert-newline="modified"}}
{{else}}
<h2 {{action "editNote" on="doubleClick"}} class="note-title" > {{title}} </h2>
<button {{action "removeNote"}} class="delete"> Delete </button>
<p {{action "editNote" on="doubleClick"}}> {{body}} </p>
{{input type="text" placeholder="URL:" class="input" value=url }}
{{/if}}
</div>
</section>
</script>
Or here the Js Bin: http://jsbin.com/oWeLuvo/1/edit?html,js,output
If my controllers or model are needed I will add that code as well.
thanks in advance
Your example seems to be working.
You just miss dependencies. You haven't included Handlebars and Ember.data
If you'd have checked your javascript console, you'd have seen the error thrown.
working example: http://jsbin.com/oWeLuvo/2/
In Ember a resource and a route are both routes. They have two names in order for Ember to differentiate between what is a resource and a route. In all honesty to remember that they are both routes and to keep your sanity you could refer to them respectively as a 'resource route' and a 'route'. A resource can be nested and have children resources or routes nested within them. Routes on the other hand cannot have nested anything.
Install the Ember Inspector if you are not already using it. It is a chrome extension and will help you with routes, controllers, templates, data and alot of other things with Ember, that you install into the Chrome Web Browser. The last that I heard the Ember Inspector is available in the FireFox Dev Tools as well. https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi?hl=en
So if have a resource, you can nest a resource, and a route. The nested resources will preserve their name space, routes will get appended to nested name space. Remember you can not nest anything within a route.
App.Router.map(function() {
//creating a resource
this.resource('foo', {path: 'somePathYouPut'}, function() {
//nesting stuff inside of the resource above
//could open another nest level here in bar if you want
this.resource('bar');
//can not nest in route. Sad face. Sorry
this.route('zoo');
});
});
Since you can not nest anything into a route your {{outlet}} in the index template does not have any children to look for and by default and render into that {{outlet}}. I believe that is what you think is going to happen.
<script type="text/x-handlebars" data-template-name="index">
<div class="wrap">
<div class="bar">
{{input type="text" class="search"
placeholder="Where is my bookmark??" value=search action="query"}}
<div class="bar-buttons">
<button {{action "addNote"}}> NEW </button>
</div>
</div>
{{outlet}}
</div>
</script>
In your code you referred to the index as a resource, its a route. Since the index is a route, remember you can not nest elements within a route, your code should have looked more like this. Also your resource 'new' path / can be removed as well.
Notes.Router.map(function () {
//this is a route not a resource you had it as resource
//and tried to nest in your code
this.route('index', { path: '/' });
this.resource('list', {path: ':note_title'});
this.resource('new');
this.resource('note', { path: ':note_id' });
});
You get an index at each nested level starting with the top most level which comes from the application level but you don't have to explicitly define them in the Router they are just there. The index route that you get for free at each nested level is associated with its immediate parent by default and render into its parents 'outlet' by default. You could think of the Router looking something like this.
For Illustrative purposes only:
Notes.Router.map(function() {
//think of this as your application level stuff that Ember just does!!
//this is not written like this but to illustrate what is going on
//you would get application template, ApplicationRoute, ApplicationController
this.resource('application', function() {
//this is an index that you get for free cause its nested
//its that index template, IndexController, and IndexRoute you were using
this.route('index', { path: '/' });
this.resource('foo', {path: 'somePathYouPutHere' }, function() {
//since you started another nested level surprise you get another `index`
//but this one is `foo/index` template.
this.route('index', {path: '/'});
this.route('zoo');
});
});
});
The first part of the above exaggerated router example, Ember does automatically behind the scenes, its part of the 'magic' you hear about. It does two things it sets up an Application environment for its self and you get ApplicationRoute, ApplicationController, and a application template which are always there behind the scene. Second it makes that index and you get IndexController, IndexRoute, and a index template that you can use or ignore. So if you do nothing else, no other code that declaring and Ember App in a file like var App = Ember.Application.create(); and open the Ember Inspector and look into routes you will see the above mentioned assets.
Now, the resource foo in the above router is an example of a resource you would make and as you see you get an index in there because you started to nest. As mentioned above you do not have to define the index at each nest level, this.route('index', {path: '/'}); from inside foo could be totally omitted and Ember will still do the same thing. You will end up with foo/index template, FooIndexRoute, FooIndexController along with the expected foo template, FooRoute, and FooController. You can think of thefooindex as a place that says 'hey' before anything else gets rolled into my parentfoo` and gets rendered I can show something if you want, use me.
This is also a good time to highlight what happens with namespace when you nest route in a resource like this.route('zoo') in the above example. The namespace of the route zoo is now going to be appended to resource foo and you end up with foo/zoo template, FooZooRoute and a FooZooController.
If you were to change zoo to a resource nested in the foo resource this.resource('zoo'); the namespace will be keep for zoo. You will end up with 'zoo' template, ZooRoute and a ZooController. The namespace is kept. Ok, enough side tracking what about your App.
You said that you wanted / url of your app to render the list template. In order to accomplish that you have to override the default behavior that happens when Ember boots up. You override the top level / by adding the {path: '/'} to the first resource or route in the Router. From the fake router code above the first index route you get is associate with the application. By default if you do nothing Ember will push that index template into the application template. However that is not what you want you want your list template to be pushed into the application template when you hit the base url of /' for your App.
Notes.Router.map(function() {
this.resource('list', {path: '/'});
this.resource('new');
this.resource('note', { path: ':note_id' });
});
By adding the code {path: '/'} to the first resource like I did above, you are now telling Ember 'hey' when my app url reaches the base url of / or when you boot up use my list template not your default index template and render that into the application template. In addition since the other resources are not nested when your App transitions to those routes via the url they will blow out whats in the application template {{outlet}} and replace themselves in there.
You mentioned about defining a "path" in a route what that does is tell Ember how you want to represent that route in the url. In the above example if you leave the new resource as is, Ember by default will use the routes name as the path, the url would be /new. You can put any name in path for the new resource, this.resource(new, {path :'emberMakesMeKingOfWorld'}); and that name will be represented in the url as /emberMakesMeKingOfWorld and that long thing will still be associated with you new route. Your user might be confused what this url means but behind the scence you would know its tied to your new route. Its just an example but probably good practice to use descriptive names so your user knows what hitting a url in your App is meant to do.
After overriding the default index template that is associated with the application. I moved your code into the application template. The reason for that it seemed as though you wanted that bar with the 'newNote' action to be present all the time. If you want something present all the time in your App, like a navigation bar, footer, im sure you can think of better stuff, place it in the application template.
Here is a JSBIN and I adjusted you code a little
http://jsbin.com/oWeLuvo/8
I hope this helps Happy Coding.

Examples on how to render a template in emberjs v1.0.0-pre.4?

I am missing something very fundamental to being able to render templates in emberjs. I found a few examples online..
github.com/elucid/ember-tunes
github.com/emberjs/examples
I'm still not able to get my stuff working. These are also using a previous version of emberjs.
ember-yii.sk.hj.cx/site/app seemed promising as well but, I was still not able to get my stuff working.
Does anyone know of examples specific to v1.0.0-pre.4 ?
I'm trying to render a header, body, and footer as separate outlets. Here is the body of app.html:
<div id="main">
<script type="text/x-handlebars" data-template-name="app">
<header>
{{outlet header}}
</header>
<section>
{{outlet body}}
</section>
<footer>
{{outlet footer}}
</footer>
</script>
</div>
What I'm not understanding is how to render templates within the 3 {{outlet}} placeholders.
I watched various EmberJS "tutorials", read the documentation. This is why I believe I'm missing something very fundamental.
Any help, guidance, or direction to helpful examples will be very appreciated.
#akshayrawat has provided an absolute gold mine at: https://gist.github.com/akshayrawat/4701510 This is literally a list of 1.0.0-pre4 EmberJS resources.
#trek, one of the core EmberJS contributors created his own version of the Todos app at: https://github.com/trek/ember-todos-with-build-tools-tests-and-other-modern-conveniences
I found this particularly useful when I was trying to figure out how to use multiple {{outlet}} placeholders.
Ps: The peepcode screencast on EmberJS costs 12 USD.. but it is really good and based on Embe 1.0.0-pre4