From dd7a318358bf8a5d2e72fa8d68fbc1d2caaaf245 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Mon, 19 May 2025 16:36:00 +0100 Subject: [PATCH 01/55] convert quick start to gjs --- guides/release/getting-started/quick-start.md | 225 +----------------- 1 file changed, 13 insertions(+), 212 deletions(-) diff --git a/guides/release/getting-started/quick-start.md b/guides/release/getting-started/quick-start.md index 424988a971..863f847560 100644 --- a/guides/release/getting-started/quick-start.md +++ b/guides/release/getting-started/quick-start.md @@ -31,7 +31,7 @@ you will have access to a new `ember` command in your terminal. You can use the `ember new` command to create a new application. ```bash -ember new ember-quickstart --lang en +ember new ember-quickstart --lang en --strict ``` This one command will create a new directory called `ember-quickstart` and set up a new Ember application inside of it. @@ -87,8 +87,6 @@ Congratulations! You just created and booted your first Ember app. ## Write some HTML in a template - - We will start by editing the `application` template. This template is always on screen while the user has your application loaded. In your editor, open `app/templates/application.gjs` and change it to the following: @@ -99,21 +97,6 @@ In your editor, open `app/templates/application.gjs` and change it to the follow {{outlet}} ``` - - - - -We will start by editing the `application` template. -This template is always on screen while the user has your application loaded. -In your editor, open `app/templates/application.hbs` and change it to the following: - -```handlebars {data-filename=app/templates/application.hbs} -

PeopleTracker

- -{{outlet}} -``` - -
Ember detects the changed file and automatically reloads the page for you in the background. You should see that the welcome page has been replaced by "PeopleTracker". @@ -135,21 +118,7 @@ ember generate route scientists You'll see output like this: - -```text -installing route - create app/routes/scientists.js - create app/templates/scientists.hbs -updating router - add route scientists -installing route-test - create tests/unit/routes/scientists-test.js -``` - - ```bash -# 🚧 Under construction 🚧 -# `ember generate route` has not been updated to produce GJS files yet. installing route create app/routes/scientists.js create app/templates/scientists.gjs @@ -158,7 +127,6 @@ updating router installing route-test create tests/unit/routes/scientists-test.js ``` - That is Ember telling you that it has created: @@ -167,20 +135,7 @@ That is Ember telling you that it has created: 3. An entry in the application's router (located in `app/router.js`). 4. A unit test for this route. - -Open the newly-created template in `app/templates/scientists.hbs` and add the following HTML: - -```handlebars {data-filename=app/templates/scientists.hbs} -{{page-title "Scientists"}} -

List of Scientists

-``` - -In your browser, open [`http://localhost:4200/scientists`](http://localhost:4200/scientists). -You should see the `

` we put in the `scientists.hbs` template right below the `

` from our `application.hbs` template. - - - -Open the newly-created template in `app/templates/scientists.gjs` and add the following HTML: +Open the newly-created template in `app/templates/scientists.gjs` and add the following code: ```gjs {data-filename=app/templates/scientists.gjs} import { pageTitle } from 'ember-page-title'; @@ -194,8 +149,6 @@ import { pageTitle } from 'ember-page-title'; In your browser, open [`http://localhost:4200/scientists`](http://localhost:4200/scientists). You should see the `

` we put in the `scientists.gjs` template right below the `

` from our `application.gjs` template. - - Since the scientist route is nested under the application route, Ember will render its content inside the application route template's `{{outlet}}` directive. Now that we've got the `scientists` template rendering, @@ -225,19 +178,6 @@ the `model()` method supports any library that uses [JavaScript Promises](https: Now let's tell Ember how to turn that array of strings into HTML. Open the `scientists` template and add the following code to loop through the array and print it: - -```handlebars {data-filename="app/templates/scientists.hbs"} -

List of Scientists

- -
    - {{#each @model as |scientist|}} -
  • {{scientist}}
  • - {{/each}} -
-``` -
- - ```gjs {data-filename="app/templates/scientists.gjs"} import { pageTitle } from 'ember-page-title'; @@ -251,7 +191,6 @@ import { pageTitle } from 'ember-page-title'; ``` - Here, we use the `each` _helper_ to loop over each item in the array we provided from the `model()` hook. Ember will render the _block_ contained @@ -275,30 +214,24 @@ As usual, there's a generator that makes this easy for us. Make a new component by typing: ```bash - -# 🚧 Under construction 🚧 -# `ember generate component` has not been updated to produce GJS files yet. - ember generate component people-list ``` - -Copy and paste the `scientists` template into the `PeopleList` component's template and edit it to look as follows: - -```handlebars {data-filename=app/components/people-list.hbs} -

{{@title}}

+You'll see output like this: -
    - {{#each @people as |person|}} -
  • {{person}}
  • - {{/each}} -
+```bash +installing component + create app/components/people-list.gjs +installing component-test + create tests/integration/components/people-list-test.gjs ``` -
+That is Ember telling you that it has created: + +1. A `people-list` component in the `components` folder +2. A `people-list-test` integration test file that you can use to test your component - -Copy and paste this part of the `scientists` template into the `PeopleList` component and edit it to look as follows: +Copy and paste this part of the `scientists` template into the newly created `PeopleList` component and edit it to look as follows: ```gjs {data-filename=app/components/people-list.gjs} ``` - - Note that we've changed the title from a hard-coded string ("List of Scientists") to `{{@title}}`. The `@` indicates that `@title` is an argument that will be passed into the component, which makes it easier to reuse the same component in @@ -322,24 +253,6 @@ other parts of the app we are building. We've also renamed `scientist` to the more-generic `person`, decreasing the coupling of our component to where it's used. - -Our component is called `PeopleList`, based on its name on the file system. Please note that the letters P and L are capitalized. - -
-
-
-
Zoey says...
-
- A component's name is derived from its file name. - We capitalize the first letter and every letter after -, then remove the hyphens. - This is known as pascal case. -
-
- -
-
-
- Save this template and switch back to the `scientists` template. We're going to tell our component: @@ -354,23 +267,6 @@ In the rest of the code examples in this tutorial, whenever we add or remove cod Let's replace all our old code with our new componentized version: - -```handlebars {data-filename="app/templates/scientists.hbs" data-diff="-1,-2,-3,-4,-5,-6,-7,+8,+9,+10,+11"} -

List of Scientists

- -
    - {{#each @model as |scientist|}} -
  • {{scientist}}
  • - {{/each}} -
- -``` -
- - ```gjs {data-filename="app/templates/scientists.gjs" data-diff="+2,-6,-7,-8,-9,-10,-11,+12,+13,+14,+15"} import { pageTitle } from 'ember-page-title'; import PeopleList from '../components/people-list'; @@ -389,7 +285,6 @@ import PeopleList from '../components/people-list'; /> ``` - Go back to your browser and you should see that the UI looks identical. The only difference is that now we've componentized our list into a version that's more reusable and more maintainable. @@ -407,20 +302,6 @@ user actions like clicks or hovers. Ember makes this easy to do. First, we can modify the `PeopleList` component to include a button: - -```handlebars {data-filename="app/components/people-list.hbs"} -

{{@title}}

- -
    - {{#each @people as |person|}} -
  • - -
  • - {{/each}} -
-``` -
- ```gjs {data-filename="app/components/people-list.gjs"} ``` - - Now that we have a button, we need to wire it up to do _something_ when a user clicks on it. For simplicity, let's say we want to show an `alert` dialog with @@ -446,79 +325,6 @@ inputs as arguments and renders them using a template. To introduce _behavior_ to our component – handling the button click in this case, we will need to attach some JavaScript to the component. - -In addition to the template, a component can also have a JavaScript file for -this exact purpose. Go ahead and create a `.js` file with the same name and in -the same directory as our template (`app/components/people-list.js`), -and paste in the following content: - -```javascript {data-filename="app/components/people-list.js"} -import Component from '@glimmer/component'; -import { action } from '@ember/object'; - -export default class PeopleListComponent extends Component { - @action - showPerson(person) { - alert(`The person's name is ${person}!`); - } -} -``` - -_Note: If you want this file created for you, you may pass the `-gc` flag when running the component generator._ - -Here, we created a basic component class and added a method that accepts a -person as an argument and brings up an alert dialog with their name. The -`@action` _decorator_ indicates we want to use this method as an _action_ -in our template, in response to user interaction. - -Now that we have implemented the desired behavior, we can go back to -the component's template and wire everything up: - -```handlebars {data-filename="app/components/people-list.hbs" data-diff="-6,+7"} -

{{@title}}

- -
    - {{#each @people as |person|}} -
  • - - -
  • - {{/each}} -
-``` - -Here, we used the `on` _modifier_ to attach the `this.showPerson` action to -the button in the template. - -There is a problem with this though – if you tried this in the browser, you -will quickly discover that clicking on the buttons will bring up an alert -dialog that said "The person's name is `[Object MouseEvent]`!" – eek! - -The cause of this bug is that we wrote our action to take an argument – the -person's name – and we forgot to pass it. The fix is easy enough: - -```handlebars {data-filename="app/components/people-list.hbs" data-diff="-6,+7"} -

{{@title}}

- -
    - {{#each @people as |person|}} -
  • - - -
  • - {{/each}} -
-``` - -Instead of passing the action to the `on` modifier directly, we used the `fn` -helper to pass the `person` as an argument which our action expects. - -Feel free to try this in the browser. Finally, everything should behave exactly -as we hoped! -
- - - Let's use the [`on` modifier](../../components/template-lifecycle-dom-and-modifiers/#toc_event-handlers) to handle click events on the button: ```gjs {data-filename="app/components/people-list.gjs"} @@ -600,11 +406,6 @@ export default class extends Component { } ``` - - - - - ## Building For Production Now that we've written our application and verified that it works in development, From 9d1b8069bd3a841b5e68106a4379f7777d6cc7c9 Mon Sep 17 00:00:00 2001 From: Ricardo Mendes Date: Mon, 2 Jun 2025 01:25:27 +0100 Subject: [PATCH 02/55] start converting a couple route files --- .../release/routing/defining-your-routes.md | 104 +++++++++++------- .../release/routing/rendering-a-template.md | 4 +- .../routing/specifying-a-routes-model.md | 75 +++++++------ 3 files changed, 113 insertions(+), 70 deletions(-) diff --git a/guides/release/routing/defining-your-routes.md b/guides/release/routing/defining-your-routes.md index dca5154556..a15c31388e 100644 --- a/guides/release/routing/defining-your-routes.md +++ b/guides/release/routing/defining-your-routes.md @@ -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. @@ -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 `` components. Multi-word route names that break this convention, such as: @@ -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 `` components. ## Nested Routes @@ -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"}} -

Posts

-{{!-- Display posts and other content --}} -{{outlet}} +```handlebars {data-filename=templates/posts.gjs} +import { pageTitle } from 'ember-page-title' + + ``` 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. @@ -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`. @@ -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"}} -

This is the posts template, containing headers to show on all child routes

-{{outlet}} +import { pageTitle } from 'ember-page-title' + + ``` -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"}} -

This is the posts/index template with a list of posts

+ ``` -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"}} -

This is an individual post, from the posts/post template, used when we enter the /posts/:post_id route

+```handlebars {data-filename=templates/posts/post.gjs} +import { pageTitle } from 'ember-page-title' + + ``` This is equivalent to having the following entry in `app/router.js` file @@ -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"}} -

This is the posts template, containing headers to show on all child routes

-

This is an individual post, from the posts/post template, used when we enter the /posts/:post_id route

+```handlebars {data-filename=templates/posts/post.gjs} +import { pageTitle } from 'ember-page-title' + + ``` When the user navigates to `/posts/`, the following markup will be seen: -```handlebars {data-filename=templates/posts/index.hbs} -{{page-title "Posts"}} -

This is the posts template, containing headers to show on all child routes

-

This is the posts/index template with a list of posts

+```handlebars {data-filename=templates/posts/index.gjs} +import { pageTitle } from 'ember-page-title' + + ``` ## Dynamic Segments @@ -322,9 +348,13 @@ Router.map(function() { }); ``` -```handlebars {data-filename=app/templates/not-found.hbs} -{{page-title "Not found"}} -

Oops, the page you're looking for wasn't found

+```handlebars {data-filename=app/templates/not-found.gjs} +import { pageTitle } from 'ember-page-title' + + ``` In the above example we have successfully used a wildcard route to handle all routes not managed by our application diff --git a/guides/release/routing/rendering-a-template.md b/guides/release/routing/rendering-a-template.md index 6a31b483d8..3a505d2ccf 100644 --- a/guides/release/routing/rendering-a-template.md +++ b/guides/release/routing/rendering-a-template.md @@ -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}}`. diff --git a/guides/release/routing/specifying-a-routes-model.md b/guides/release/routing/specifying-a-routes-model.md index f5410b2bfe..b81b5a05b7 100644 --- a/guides/release/routing/specifying-a-routes-model.md +++ b/guides/release/routing/specifying-a-routes-model.md @@ -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|}} -
- {{post.title}} -
-{{/each}} +```handlebars {data-filename=app/templates/favorite-posts.gjs} + ``` 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. @@ -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} -

Playlist

+```handlebars {data-filename=app/templates/songs.gjs} + ``` ## Dynamic Models @@ -216,12 +220,16 @@ instead. When you provide a string or number to the ``, 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|}} - - link text to display - -{{/each}} +```handlebars {data-filename=app/templates/photos.gjs} +import { LinkTo } from '@ember/routing'; + + ``` However, if you provide the entire model context, the model hook for that URL segment will _not_ be run. @@ -229,18 +237,23 @@ For this reason, many Ember developers choose to pass only ids to `` so Here's what it looks like to pass the entire `photo` record: -```handlebars {data-filename=app/templates/photos.hbs} -{{#each @model as |photo|}} - - link text to display - -{{/each}} +```handlebars {data-filename=app/templates/photos.gjs} +import { LinkTo } from '@ember/routing'; + + ``` 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 link text to display From b37e495d4e69e954e352e7439af3d33b8ca0c25e Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Fri, 27 Jun 2025 16:24:46 +0100 Subject: [PATCH 03/55] update all handlebars code blocks to gjs --- guides/release/routing/defining-your-routes.md | 14 +++++++------- .../release/routing/specifying-a-routes-model.md | 9 ++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/guides/release/routing/defining-your-routes.md b/guides/release/routing/defining-your-routes.md index a15c31388e..5f34094e63 100644 --- a/guides/release/routing/defining-your-routes.md +++ b/guides/release/routing/defining-your-routes.md @@ -109,7 +109,7 @@ 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.gjs} +```gjs {data-filename=templates/posts.gjs} import { pageTitle } from 'ember-page-title'