In emberjs, I am in a situation that my application already has routes template that uses the application.hbs template, but now I want to create a new route templates that doesn't use application.hbs.
Is there any easy solution for that?
I have seen many answers but that doesn't match my specification and also my version of ember is 2.11.
Thank you.
Keep application.hbs as minimal and common across all routes as you can. What you shoud do is generate top level routes for your application.
Say you have a common setup where you have an authenticated section, post login, and a login section. It is common for pre and post login to have differing top level templates. Try something like this:
ember g route login
ember g route login/index
ember g route login/forgot-password
ember g route authenticated
ember g route authenticated/index
ember g route authenticated/profile
etc...
Your login.hbs would have its own style, and potentially child routes, which will assume that style and place subsequent nested templates in the {{outlet}} that others have mentioned.
File Structure:
routes/
-login/
----index.hbs
----forgot-password.hbs
-authenticated/
----index.hbs
----profile.hbs
login.hbs
authenticated.hbs
application.hbs
In the example above, login.hbs might look like this:
{{yellow-navbar}}
{{outlet}}
and authenticated.hbs like this:
{{pink-navbar}}
{{user.name}}
{{outlet}}
Now, the login/index.hbs and login/forgot-password.hbs templates will render in the login.hbs outlet. both of these pages will render a yellow navbar, and then their own content.
Because authenticated.hbs is another top level parent route, both authenticated/index.hbs and authenticated/profile.hbs will render their content beneath a pink navbar and a display of the current user's name.
if your application.hbs looked like this:
{{#general-site-container}}
<h2>Website Name</h2>
{{outlet}}
{{/general-site-container}}
Then all of the routes, both authenticated and login, will be in the general-site-container, and will all show the h2 with the website name.
An important note in this, and something that I see a lot of people get confused with, is that this folder structure does not dictate the actual path of the route.
The router might be configured like this, to avoid showing "authenticated" in the url:
Router.js
// login.index route assumed at root url /login
this.route('login', { path: '/login' }, function() {
// avail at /login/forgot-password
this.route('forgot-password', { path: '/forgot-password' }
});
//authenticated.index.hbs assumed at root avail at /
//notice that the authenticated parent route has a route of "/"
this.route('authenticated', { path: '/' }, function() {
// authenticated.profile route avail at /profile
this.route('profile', { path: '/profile' });
// as an exmaple you can further nest content to your desire
// if for instance your profile personal info section has a parent
// template/route
this.route('', { path: '/personal-info' }, function() {
this.route('address', { path: '/address' }); //profile/personal-info/address
});
});
i think you need to use {{outlet}} for achieve this.
create diffrent outlets where you need to show diffrent templates by overriding application template
{{outlet}} //application hbs default one
{{outlet "view1"}} // another template
{{outlet "view2"}} //another template
there should be view1.hbs and view2.hbs in order to render those templates
Related
I currently trying use LinkTo to render another nested route by using {{outlet}} tag to another new page.
I have a forum route and nested forum details route
In the forum template:
<LinkTo #route="main.forum.forum-details" #model={{post}}>{{post.title}}</LinkTo>
{{outlet}}
As the image above. The nested route will be render at the bottom instead of to the new page. How am i going to render it to new page? I was thinking LinkTo will actually link it to the new page right? And the forum details should be render at the {{outlet}} tag, so where should I place the {{outlet}} tag in order to let it render to new page?
You problem is that you need to understand nested routes.
If your route is main.forum.forum-details this means your router looks like this:
this.route('main', function() {
this.route('forum', function() {
this.route('forum-details');
});
});
So the forum route is a parent route of the forum-details route.
And its important to understand that parent routes are always visible when visiting a child route.
So for the main.forum.forum-details ember will render your application route and inside its {{outlet}} it will render the forum route and inside this {{outlet}} it will render the forum-details route.
So if you want either the forum or the forum-details route you can restructure your routes:
this.route('main', function() {
this.route('forum');
this.route('forum-details');
});
or you can move what you currently have in your forum route to your forum.index route. If a route has subroutes and none of the subroutes is active there will always be an index route active that you can use.
On Ember 3.15 (Octane)
I'm trying to create the following routes in my application
/cars
/cars/:car_id
/cars/:car_id/:model_id
The first two are fine and I've been able to create them using ember-cli
ember g route cars
ember g route cars/car
The problem with the third one is that I need the model template to be shown under the cars template, ie. replace the car template. So if I put an {{outlet}} on the car template, the model template is rendered in it fine but obviously with that {{outlet}} nothing happens when I navigate to /cars/5/2
My router looks like this
Router.map(function() {
this.route('cars', function(){
this.route('car', { path: ':car_id'}, function() {
this.route('model', { path: ':model_id' });
});
});
});
I've tried using an index route ember g route cars/car/index but the problem with this that the params are not available in the index route and can only be accessed on the car route.
As plan b I've created the car and model routes as top level routes (based on this answer), but I'd like to achieve the above because of a few reasons
it seems more logical ie, structuring the app based on the nesting of the routes
I have many nested routes and creating all of them as first level routes will become hard to maintain
ember doesn't apply the active class correctly with this configuration. For example if I have a navbar with Cars as an link, I'd want it to have the active styling on all three pages. But this doesn't work anymore because the second route will be called something like car and the third one something like model.
there are some issues with the urls that are created by default using <LinkTo />.
So if I have something like this in my car template
<ul>
{{#each #model.models as |model|}}
<li><LinkTo #route="model" #model={{model}}> {{model.title}} </LinkTo></li>
{{/each}}
</ul>
The actual link works properly in that it takes me to the correct model page, however, the url is shown as /cars/undefined/undefined. (Although this is fixable by passing #models={{array #model.id model.id}}, it seems like a workaround at this point)
The solution is indeed to use the index route.
So have cars.car.index and cars.car.model. However you should use the cars.car route to load the common car model. Then you can access it in the cars.car.index route with this.modelFor('cars.car').
Then you can access the params.car_id in the cars.car route, use it to load the car, and then access this car from the cars.car.index and cars.car.model routes with modelFor.
I want to add the component's name to the URL in Ember js.
Example:
If the main URL is : localhost:4200/home ,
then in home,if I call/redirect to a component(say - abc), the URL should get appended:
localhost:4200/abc
or
localhost:4200/home/abc
If the main URL is : localhost:4200/home , then in home,if I
call/redirect to a component(say - abc), the URL should get appended:
How do you call/redirect to a component in ember?. If you got some hacky way to do that, then I will encourage you not to do this.
In Ember, the router matches the current URL to the routes that you've defined.
If you got /home/abc URL path, it means you have abc route nested inside home route.
Router.map(function() {
this.route('home', function() {
this.route('abc');
});
});
If you know all routes will come under home route, then you can define it manually. Suppose if you don't know or what will come , then go for defining the dynamic segments in URL.
You can do it by {{component}} helper.
You need to define dynamic path for home and send the component name from url to home controller by model hook.
Below are essential parts of the solution.
//router.js
this.route('home', {path: 'home/:cmp'});
//routes/home.js
model(params){
return params.cmp;
}
//templates/home.hbs
{{component model}}
Please take a look at this tiwddle
I have a componente called hero (in application.hbs) and I wish display this componente only in home page.
I researched about how do this but without any success. Thanks!
After a few minutes and some searches on GitHub...
Just install ember install ember-truth-helpers and check the route name:
{{#if (eq currentRouteName 'index')}}
{{hero}}
{{/if}}
Glad to help!
I need more specifics, however, I am going to make the assumption that your home route is the '/' route.
The '/' route is actually your index route, so if you create an index.hbs file it will act as the template for your index route. And then your should just move the hero component to your index.hbs file.
I can't be sure your reasons, but I suspect that this could be a solution.
There is an invisible 'application' route... there is also an implicit 'index' route, but you can skip the confusion of that and just create a 'home' route and give it a path to the root. The application template will house the outlet - and then you can place your component just in the 'home' template;
(don't write an application route like this, but just for visualization)
Router.map(function() {
// overarching 'application' route
this.route('application', function() {
this.route('home', { path: '/' });
this.route('other');
});
});
Here is a twiddle with the full example in place. If this doesn't do what you want, then refer to the conditional suggestions. : )
Router.map(function() {
// here's an example of skipping of skipping the mysterious 'index' in another situation
this.route('books', function() {
this.route('books-list', { path: '/' });
this.route('book');
});
});
You can also render a component dynamically using component helper which save you a conditional statement inside your template.
The first parameter of the helper is the name of a component to render, as a string. So {{component 'blog-post'}} is just the same as using {{blog-post}}.
When the parameter passed to {{component}} evaluates to null or undefined, the helper renders nothing. When the parameter changes, the currently rendered component is destroyed and the new component is created and brought in.
So you can safely pass in anything to the component helper, in your case you can make the component name dynamically without worry an error will raised.
https://guides.emberjs.com/v2.1.0/components/defining-a-component/#toc_dynamically-rendering-a-component
Can someone explain in layman's terms the way nested outlets work in ember templates?
In particular trying understand this from the docs:
http://emberjs.com/guides/routing/rendering-a-template/
"The immediate parent route did not render into the main outlet ..."
This means that the current route tried to render into the parent
route's template, but the parent route didn't render a template, or,
if it did, that the template which the parent route provided did not
render into the main template (i.e., a default {{outlet}}).
More specifically I am trying understand how to create a nested view hierarchy in my app.
It is three layers deep of collections. I want to create a series of collapsible nested views, based on the contents of the collections. The data structure could be tree like.
Libraries -> each Library has many Books -> each Book has many Pages
Looking for an illustrative jsbin or code sample that demonstrates the nested template structure in practice.
Imagine a router that looks like this:
App.Router.map(function() {
this.resource('libraries', function() {
this.route('new');
this.resource('library', {path: ':library_id'}, function() {
this.resource('books', function() {
this.route('new');
this.resource('book', {path: ':book_id'}, function() {
this.resource('pages', function() {
this.route('new');
this.resource('page', {path: ':page_id'}, function() {
}); // Page
}); // Pages
}); // Book
}); // Books
}); // Library
}); // Libraries
}); // map
In general most templating languages provide some way to wrap the target content of a page into a main layout. This allows separation of the common page layout into another file and the smaller target template in a different file.
There have been a few iterations of this in Ember, currently this functionality is provided by the {{outlet}} helper. Outlets are Ember's way to yield into a layout.
The area where outlet diverges significantly from yield is nesting. Yielding on server-side is much simpler. You only need to mark areas of a template to yield into and then call to yield a block of content into that designated target.
However when the rendering of content is switched to client-side javascript, only parts of a page are updated on demand. You can no longer simply yield directly into markers. You need a smarter yield ie:- outlet.
There are 2 sides to an {{outlet}}.
A marker that indicates where you want to yield. This is the {{outlet}} helper.
Code that renders a template into this outlet. This is the render method used inside the renderTemplate hook.
By default an {{outlet}} does not need a name. This makes it the default outlet for that template. There can be many such outlets in a template and they can be specified by giving it a name. For eg:-
{{outlet 'sidebar'}}
{{outlet 'nav'}}
This declares 2 outlets named 'sidebar' and 'nav'. You can now render other templates into these outlets.
Default outlets are used when rendering without an explicit outlet name. For named outlets the rendering is done by calling render in a renderTemplate hook of a Route.
You do this by specifying an outlet option in a hash passed to the render method as options.
renderTemplate() {
this.render('recentPosts', { outlet: 'sidebar' });
}
Here, the template recentPosts will be rendered into an outlet named 'sidebar' inside its parent template.
When routes are nested inside other nested routes they will render into the nearest parent outlet. If the parent resource doesn't have a default outlet then it's parent is used, and so on until the application template is reached.
When you declare a resource with this.resource('posts'); in the Router, you are indicating a few things based on convention.
Render the posts route with the layout template posts.
Optionally, render the implicit posts.index route with the template posts/index.
The posts template contains layout common to all posts and it's sub resources. At the bare minimum it must contain at least a default outlet like, {{outlet}}.
Without this {{outlet}} child routes will not have an immediate parent outlet to render into. They will then render in that parent's parent or ultimately the application template's outlet. When this happens you will see the "The immediate parent route did not render into the main outlet ..." warning. Check the location of your outlets when this happens.
The posts.index is an implicit route given to all resources that have nested routes. In other words if your resource has nested routes, you don't need to explicitly declare a nested, this.route('index)`.
This index route can display the content of that resource. For instance, for posts.index, You can display a list of all posts. One secondary caveat with this implicit route is that the model is on the parent posts route. You have to use the needs api to get at this model in the PostsIndexController.
needs: ['posts'],
contentBinding: 'controller.posts'
Further, this posts.index route is optional. You can place the UI from posts/index used to display a list of posts, directly into the posts template itself. However this means any child resource will also render with the list of posts, along side the outlet in posts. The decision whether to use an explicit index route or not depends on the UI that needs to be displayed.
Sitting above all other templates is the application template. It must have an outlet for nested resources to render in, and will typically house the layout common to the page. If you don't specify an application template a default template will be used. This generated template is equivalent to {{outlet}}, ie:- a template with just a default outlet.
Consider the following routes.
App.Router.map(function() {
this.resource('posts', function() {
this.route('new')
this.resource('post', {path: ':post_id'}, function() {
this.resource('comments', function() {
this.route('new');
});
});
});
});
Here, posts.new will be rendered into posts which will be rendered inside posts, which will be rendered into the application template's default outlet. The rest of the templates used are listed below.
+---------------------------+--------------------------------------------------------+
| Route | Templates used (default outlets) |
+---------------------------+--------------------------------------------------------+
| posts.index | posts.index > posts > application |
+---------------------------+--------------------------------------------------------+
| posts.new | posts.new > posts > application |
+---------------------------+--------------------------------------------------------+
| posts.post.index | post.index > post > posts > application |
+---------------------------+--------------------------------------------------------+
| posts.post.new | post.new > post > posts > application |
+---------------------------+--------------------------------------------------------+
| posts.post.comments.index | comments.index > comments > post > posts > application |
+---------------------------+--------------------------------------------------------+
| posts.post.comments.new | comments.new > comments > post > posts > application |
+---------------------------+--------------------------------------------------------+
This default template hierarchy can be changed by specifying an into option to the render method.
renderTemplate: function() {
this.render('posts', { into: 'sidebar' })
}
Here the posts template will render into the default outlet of the sidebar template.
That's about it. Outlet is another ember concept that uses a good deal of convention over configuration. The defaults are quite good, at the same time easy to customize.