Skip to content

Convert routing section to route components #2118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 67 additions & 37 deletions guides/release/routing/defining-your-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ To define a route, run
ember generate route route-name
```

This creates a route file at `app/routes/route-name.js`, a template for the route at `app/templates/route-name.hbs`,
This creates a route file at `app/routes/route-name.js`, a template for the route at `app/templates/route-name.gjs`,
and a unit test file at `tests/unit/routes/route-name-test.js`.
It also adds the route to the router.

Expand Down Expand Up @@ -65,7 +65,7 @@ Router.map(function() {
```

The route defined above will by default use the `blog-post.js` route handler,
the `blog-post.hbs` template, and be referred to as `blog-post` in any
the `blog-post.gjs` template, and be referred to as `blog-post` in any
`<LinkTo />` components.

Multi-word route names that break this convention, such as:
Expand All @@ -77,7 +77,7 @@ Router.map(function() {
```

will still by default use the `blog-post.js` route handler and the
`blog-post.hbs` template, but will be referred to as `blog_post` in any
`blog-post.gjs` template, but will be referred to as `blog_post` in any
`<LinkTo />` components.

## Nested Routes
Expand Down Expand Up @@ -109,17 +109,22 @@ ember generate route posts/new
And then add the `{{outlet}}` helper to your template where you want the nested
template to display. You can also add a page title with the current page name (using [page-title helper](../../accessibility/page-template-considerations/#toc_page-title)), this will help users with assistive technology know where they are in the website.

```handlebars {data-filename=templates/posts.hbs}
{{page-title "Posts - Site Title"}}
<h1>Posts</h1>
{{!-- Display posts and other content --}}
{{outlet}}
```handlebars {data-filename=templates/posts.gjs}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still in draft I see, but just quickly noticed this should be gjs instead of handlebars for all code blocks.

import { pageTitle } from 'ember-page-title'

<template>
{{pageTitle "Posts - Site Title"}}

<h1>Posts</h1>
{{!-- Display posts and other content --}}
{{outlet}}
</template>
```

This generates a route for `/posts` and for `/posts/new`. When a user
visits `/posts`, they'll simply see the `posts.hbs` template. (Below, [index
visits `/posts`, they'll simply see the `posts.gjs` template. (Below, [index
routes](#toc_index-routes) explains an important addition to this.) When the
user visits `posts/new`, they'll see the `posts/new.hbs` template rendered into
user visits `posts/new`, they'll see the `posts/new.gjs` template rendered into
the `{{outlet}}` of the `posts` template.

A nested route name includes the names of its ancestors.
Expand All @@ -134,7 +139,7 @@ routes, it will load a template with the same name (`application` in
this case) by default.
You should put your header, footer, and any other decorative content
here. All other routes will render
their templates into the `application.hbs` template's `{{outlet}}`.
their templates into the `application.gjs` template's `{{outlet}}`.

This route is part of every application, so you don't need to
specify it in your `app/router.js`.
Expand Down Expand Up @@ -200,38 +205,51 @@ replace the `{{outlet}}` in the `posts` template with the

The following scenarios may help with understanding the `index` route:

- The top-level index route is analogous to `index.html`. For example, when someone visits `https://some-ember-app.com`, the contents of the `template/index.hbs` file will be rendered. There is no need to add an entry `this.route('index', { path: '/' });` in `app/router.js` file. The `index` route is implicitly included in order to help reduce verbose declarations in the `app/router.js`. The `app/router.js` file could be empty, and the `index` would still be shown:
- The top-level index route is analogous to `index.html`. For example, when someone visits `https://some-ember-app.com`, the contents of the `template/index.gjs` file will be rendered. There is no need to add an entry `this.route('index', { path: '/' });` in `app/router.js` file. The `index` route is implicitly included in order to help reduce verbose declarations in the `app/router.js`. The `app/router.js` file could be empty, and the `index` would still be shown:

```javascript {data-filename=app/router.js}
Router.map(function() {
});
```
- When a user navigates to `/posts`, the contents of `index.hbs` will be rendered. This is similar to a user navigating to the child route of `/posts`. `/posts/index` is a child route like `/posts/comments` or `/posts/likes`.
- When a user navigates to `/posts`, the contents of `index.gjs` will be rendered. This is similar to a user navigating to the child route of `/posts`. `/posts/index` is a child route like `/posts/comments` or `/posts/likes`.

### When to use an index route

The index route is most helpful for rendering a view when the route has [dynamic segments](#toc_dynamic-segments) defined in it or there are nested routes. In other words, an `index` template is used to show content that should not be present on sibling and child routes. For example, a blog app might have an `index` route that shows a list of all posts, but if a user clicks on a post, they should only see the content for the individual post. Here is how that looks in practice:

A `templates/posts.hbs` file has the following:
A `templates/posts.gjs` file has the following:

```handlebars {data-filename=templates/posts.gjs}

```handlebars {data-filename=templates/posts.hbs}
{{page-title "Posts"}}
<h1>This is the posts template, containing headers to show on all child routes</h1>
{{outlet}}
import { pageTitle } from 'ember-page-title'

<template>
{{pageTitle "Posts"}}
<h1>This is the posts template, containing headers to show on all child routes</h1>
{{outlet}}
</template>
```

The `templates/posts/index.hbs` file has the following:
The `templates/posts/index.gjs` file has the following:

```handlebars {data-filename=templates/posts/index.gjs}
import { pageTitle } from 'ember-page-title'

```handlebars {data-filename=templates/posts/index.hbs}
{{page-title "Posts"}}
<p>This is the posts/index template with a list of posts</p>
<template>
{{pageTitle "Posts"}}
<p>This is the posts/index template with a list of posts</p>
</template>
```

The `templates/posts/post.hbs` file has the following:
The `templates/posts/post.gjs` file has the following:

```handlebars {data-filename=templates/posts/post.hbs}
{{page-title "Post"}}
<p>This is an individual post, from the posts/post template, used when we enter the /posts/:post_id route</p>
```handlebars {data-filename=templates/posts/post.gjs}
import { pageTitle } from 'ember-page-title'

<template>
{{pageTitle "Post"}}
<p>This is an individual post, from the posts/post template, used when we enter the /posts/:post_id route</p>
</template>
```

This is equivalent to having the following entry in `app/router.js` file
Expand All @@ -247,18 +265,26 @@ Router.map(function() {

When the user navigates to `/posts/123`, the following markup will be seen:

```handlebars {data-filename=templates/posts/post.hbs}
{{page-title "Posts"}}
<h1>This is the posts template, containing headers to show on all child routes</h1>
<p>This is an individual post, from the posts/post template, used when we enter the /posts/:post_id route</p>
```handlebars {data-filename=templates/posts/post.gjs}
import { pageTitle } from 'ember-page-title'

<template>
{{pageTitle "Posts"}}
<h1>This is the posts template, containing headers to show on all child routes</h1>
<p>This is an individual post, from the posts/post template, used when we enter the /posts/:post_id route</p>
</template>
```

When the user navigates to `/posts/`, the following markup will be seen:

```handlebars {data-filename=templates/posts/index.hbs}
{{page-title "Posts"}}
<h1>This is the posts template, containing headers to show on all child routes</h1>
<p>This is the posts/index template with a list of posts</p>
```handlebars {data-filename=templates/posts/index.gjs}
import { pageTitle } from 'ember-page-title'

<template>
{{pageTitle "Posts"}}
<h1>This is the posts template, containing headers to show on all child routes</h1>
<p>This is the posts/index template with a list of posts</p>
</template>
```

## Dynamic Segments
Expand Down Expand Up @@ -322,9 +348,13 @@ Router.map(function() {
});
```

```handlebars {data-filename=app/templates/not-found.hbs}
{{page-title "Not found"}}
<p>Oops, the page you're looking for wasn't found</p>
```handlebars {data-filename=app/templates/not-found.gjs}
import { pageTitle } from 'ember-page-title'

<template>
{{pageTitle "Not found"}}
<p>Oops, the page you're looking for wasn't found</p>
</template>
```

In the above example we have successfully used a wildcard route to handle all routes not managed by our application
Expand Down
4 changes: 2 additions & 2 deletions guides/release/routing/rendering-a-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ the `posts.new` route will render `posts/new.hbs`.

Each template will be rendered into the `{{outlet}}` of its parent route's
template. For example, the `posts.new` route will render its template into the
`posts.hbs`'s `{{outlet}}`, and the `posts` route will render its template into
the `application.hbs`'s `{{outlet}}`.
`posts.gjs`'s `{{outlet}}`, and the `posts` route will render its template into
the `application.gjs`'s `{{outlet}}`.
75 changes: 44 additions & 31 deletions guides/release/routing/specifying-a-routes-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ export default class FavoritePostsRoute extends Route {

Now that data can be used in the `favorite-posts` template:

```handlebars {data-filename=app/templates/favorite-posts.hbs}
{{#each @model as |post|}}
<div>
{{post.title}}
</div>
{{/each}}
```handlebars {data-filename=app/templates/favorite-posts.gjs}
<template>
{{#each @model as |post|}}
<div>
{{post.title}}
</div>
{{/each}}
</template>
```

Behind the scenes, what is happening is that the [route's controller](https://api.emberjs.com/ember/release/classes/Route/methods/setupController?anchor=setupController) receives the results of the model hook, and Ember makes the model hook results available to the template. Your app may not have a controller file for the route, but the behavior is the same regardless.
Expand Down Expand Up @@ -133,22 +135,24 @@ export default class SongsRoute extends Route {
In the `songs` template, we can specify both models and use the `{{#each}}` helper to display
each record in the song model and album model:

```handlebars {data-filename=app/templates/songs.hbs}
<h1>Playlist</h1>
```handlebars {data-filename=app/templates/songs.gjs}
<template>
<h1>Playlist</h1>

<ul>
{{#each @model.songs as |song|}}
<li>{{song.name}} by {{song.artist}}</li>
{{/each}}
</ul>
<ul>
{{#each @model.songs as |song|}}
<li>{{song.name}} by {{song.artist}}</li>
{{/each}}
</ul>

<h1>Albums</h1>
<h1>Albums</h1>

<ul>
{{#each @model.albums as |album|}}
<li>{{album.title}} by {{album.artist}}</li>
{{/each}}
</ul>
<ul>
{{#each @model.albums as |album|}}
<li>{{album.title}} by {{album.artist}}</li>
{{/each}}
</ul>
</template>
```

## Dynamic Models
Expand Down Expand Up @@ -216,31 +220,40 @@ instead.
When you provide a string or number to the `<LinkTo>`, the dynamic segment's `model` hook will run when the app transitions to the new route.
In this example, `photo.id` might have an id of `4`:

```handlebars {data-filename=app/templates/photos.hbs}
{{#each @model as |photo|}}
<LinkTo @route="photo" @model={{photo.id}}>
link text to display
</LinkTo>
{{/each}}
```handlebars {data-filename=app/templates/photos.gjs}
import { LinkTo } from '@ember/routing';

<template>
{{#each @model as |photo|}}
<LinkTo @route="photo" @model={{photo.id}}>
link text to display
</LinkTo>
{{/each}}
</template>
```

However, if you provide the entire model context, the model hook for that URL segment will _not_ be run.
For this reason, many Ember developers choose to pass only ids to `<LinkTo>` so that the behavior is consistent.

Here's what it looks like to pass the entire `photo` record:

```handlebars {data-filename=app/templates/photos.hbs}
{{#each @model as |photo|}}
<LinkTo @route="photo" @model={{photo}}>
link text to display
</LinkTo>
{{/each}}
```handlebars {data-filename=app/templates/photos.gjs}
import { LinkTo } from '@ember/routing';

<template>
{{#each @model as |photo|}}
<LinkTo @route="photo" @model={{photo}}>
link text to display
</LinkTo>
{{/each}}
</template>
```

If you decide to pass the entire model, be sure to cover this behavior in your [application tests](../../testing/testing-application/).

If a route you are trying to link to has multiple dynamic segments, like `/photos/4/comments/18`, be sure to specify all the necessary information for each segment:

TODO(locks)
```handlebars
<LinkTo @route="photos.photo.comments.comment" @models={{array 4 18}}>
link text to display
Expand Down