Parent Template Renders `{{outlet}}`, Children Fill It In
Nested Routes
Parent routes can render shared markup; child routes are rendered inside the parent's `{{outlet}}`. Perfect for sidebars, tabs, and dashboards.
What you'll learn
- Nest routes in the router
- Use `{{outlet}}` in a parent template
- Apply a shared layout via a parent route
Nested routes let you share layout, data, and authorization across a family of pages. The parent’s template renders once, and child templates plug in via {{outlet}}.
Declare Nested Routes
// app/router.js
Router.map(function () {
this.route('admin', function () {
this.route('users');
this.route('posts');
this.route('settings');
});
}); This produces URLs /admin/users, /admin/posts, /admin/settings and a /admin index. The route files live at app/routes/admin.js, app/routes/admin/users.js, and so on.
Render the Parent Layout
{{! app/templates/admin.hbs }}
<div class="admin-layout">
<aside class="sidebar">
<LinkTo @route="admin.users">Users</LinkTo>
<LinkTo @route="admin.posts">Posts</LinkTo>
<LinkTo @route="admin.settings">Settings</LinkTo>
</aside>
<main class="content">
{{outlet}}
</main>
</div> {{outlet}} is where the active child renders.
Child Template
{{! app/templates/admin/users.hbs }}
<h1>Users</h1>
<table>
{{#each @model as |user|}}
<tr><td>{{user.name}}</td><td>{{user.email}}</td></tr>
{{/each}}
</table> Parent Data Reaches Children
Models loaded by the parent are available to children via this.modelFor('admin'):
// app/routes/admin/users.js
import Route from '@ember/routing/route';
export default class AdminUsersRoute extends Route {
async model() {
const admin = this.modelFor('admin'); // the parent's model
return this.store.query('user', { tenant: admin.tenantId });
}
}