Reputation: 1223
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
Upvotes: 0
Views: 451
Reputation: 681
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 the
fooindex as a place that says 'hey' before anything else gets rolled into my parent
foo` 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
I hope this helps Happy Coding.
Upvotes: 1
Reputation: 44589
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/
Upvotes: 1