@@ -135,6 +135,7 @@ ember generate route scientists
135135
136136You'll see output like this:
137137
138+ <feature-flag-off-template-tag >
138139``` text
139140installing route
140141 create app/routes/scientists.js
@@ -144,6 +145,20 @@ updating router
144145installing route-test
145146 create tests/unit/routes/scientists-test.js
146147```
148+ </feature-flag-off-template-tag >
149+ <feature-flag-on-template-tag >
150+ ``` bash
151+ # 🚧 Under construction 🚧
152+ # `ember generate route` has not been updated to produce GJS files yet.
153+ installing route
154+ create app/routes/scientists.js
155+ create app/templates/scientists.gjs
156+ updating router
157+ add route scientists
158+ installing route-test
159+ create tests/unit/routes/scientists-test.js
160+ ```
161+ </feature-flag-on-template-tag >
147162
148163That is Ember telling you that it has created:
149164
@@ -152,6 +167,7 @@ That is Ember telling you that it has created:
1521673 . An entry in the application's router (located in ` app/router.js ` ).
1531684 . A unit test for this route.
154169
170+ <feature-flag-off-template-tag >
155171Open the newly-created template in ` app/templates/scientists.hbs ` and add the following HTML:
156172
157173``` handlebars {data-filename=app/templates/scientists.hbs}
@@ -162,6 +178,24 @@ Open the newly-created template in `app/templates/scientists.hbs` and add the fo
162178In your browser, open [ ` http://localhost:4200/scientists ` ] ( http://localhost:4200/scientists ) .
163179You should see the ` <h2> ` we put in the ` scientists.hbs ` template right below the ` <h1> ` from our ` application.hbs ` template.
164180
181+ </feature-flag-off-template-tag >
182+ <feature-flag-on-template-tag >
183+ Open the newly-created template in ` app/templates/scientists.gjs ` and add the following HTML:
184+
185+ ``` gjs {data-filename=app/templates/scientists.gjs}
186+ import { pageTitle } from 'ember-page-title';
187+
188+ <template>
189+ {{pageTitle "Scientists"}}
190+ <h2>List of Scientists</h2>
191+ </template>
192+ ```
193+
194+ In your browser, open [ ` http://localhost:4200/scientists ` ] ( http://localhost:4200/scientists ) .
195+ You should see the ` <h2> ` we put in the ` scientists.gjs ` template right below the ` <h1> ` from our ` application.gjs ` template.
196+
197+ </feature-flag-on-template-tag >
198+
165199Since the scientist route is nested under the application route, Ember will render its content inside the application route template's ` {{outlet}} ` directive.
166200
167201Now that we've got the ` scientists ` template rendering,
@@ -191,6 +225,7 @@ the `model()` method supports any library that uses [JavaScript Promises](https:
191225Now let's tell Ember how to turn that array of strings into HTML.
192226Open the ` scientists ` template and add the following code to loop through the array and print it:
193227
228+ <feature-flag-off-template-tag >
194229``` handlebars {data-filename="app/templates/scientists.hbs"}
195230<h2>List of Scientists</h2>
196231
@@ -200,6 +235,23 @@ Open the `scientists` template and add the following code to loop through the ar
200235 {{/each}}
201236</ul>
202237```
238+ </feature-flag-off-template-tag >
239+
240+ <feature-flag-on-template-tag >
241+ ``` gjs {data-filename="app/templates/scientists.gjs"}
242+ import { pageTitle } from 'ember-page-title';
243+
244+ <template>
245+ {{pageTitle "Scientists"}}
246+ <h2>List of Scientists</h2>
247+ <ul>
248+ {{#each @model as |scientist|}}
249+ <li>{{scientist}}</li>
250+ {{/each}}
251+ </ul>
252+ </template>
253+ ```
254+ </feature-flag-on-template-tag >
203255
204256Here, we use the ` each ` _ helper_ to loop over each item in the array we
205257provided from the ` model() ` hook. Ember will render the _ block_ contained
@@ -217,16 +269,21 @@ As your application grows, you will notice you are sharing UI elements between m
217269or using them multiple times on the same page.
218270Ember makes it easy to refactor your templates into reusable components.
219271
220- Let's create a ` < PeopleList> ` component that we can use in multiple places to show a list of people.
272+ Let's create a ` PeopleList ` component that we can use in multiple places to show a list of people.
221273
222274As usual, there's a generator that makes this easy for us.
223275Make a new component by typing:
224276
225277``` bash
278+ < feature-flag-on-template-tag>
279+ # 🚧 Under construction 🚧
280+ # `ember generate component` has not been updated to produce GJS files yet.
281+ < /feature-flag-on-template-tag>
226282ember generate component people-list
227283```
228284
229- Copy and paste the ` scientists ` template into the ` <PeopleList> ` component's template and edit it to look as follows:
285+ <feature-flag-off-template-tag >
286+ Copy and paste the ` scientists ` template into the ` PeopleList ` component's template and edit it to look as follows:
230287
231288``` handlebars {data-filename=app/components/people-list.hbs}
232289<h2>{{@title}}</h2>
@@ -238,6 +295,25 @@ Copy and paste the `scientists` template into the `<PeopleList>` component's tem
238295</ul>
239296```
240297
298+ </feature-flag-off-template-tag >
299+
300+ <feature-flag-on-template-tag >
301+ Copy and paste this part of the ` scientists ` template into the ` PeopleList ` component and edit it to look as follows:
302+
303+ ``` gjs {data-filename=app/components/people-list.gjs}
304+ <template>
305+ <h2>{{@title}}</h2>
306+
307+ <ul>
308+ {{#each @people as |person|}}
309+ <li>{{person}}</li>
310+ {{/each}}
311+ </ul>
312+ </template>
313+ ```
314+
315+ </feature-flag-on-template-tag >
316+
241317Note that we've changed the title from a hard-coded string ("List of Scientists")
242318to ` {{@title}} ` . The ` @ ` indicates that ` @title ` is an argument that will be
243319passed into the component, which makes it easier to reuse the same component in
@@ -246,7 +322,8 @@ other parts of the app we are building.
246322We've also renamed ` scientist ` to the more-generic ` person ` ,
247323decreasing the coupling of our component to where it's used.
248324
249- Our component is called ` <PeopleList> ` , based on its name on the file system. Please note that the letters P and L are capitalized.
325+ <feature-flag-off-template-tag >
326+ Our component is called ` PeopleList ` , based on its name on the file system. Please note that the letters P and L are capitalized.
250327
251328<div class =" cta " >
252329 <div class =" cta-note " >
@@ -261,6 +338,7 @@ Our component is called `<PeopleList>`, based on its name on the file system. Pl
261338 <img src="/images/mascots/zoey.png" role="presentation" alt="">
262339 </div >
263340</div >
341+ </feature-flag-off-template-tag >
264342
265343Save this template and switch back to the ` scientists ` template.
266344
@@ -276,6 +354,7 @@ In the rest of the code examples in this tutorial, whenever we add or remove cod
276354
277355Let's replace all our old code with our new componentized version:
278356
357+ <feature-flag-off-template-tag >
279358``` handlebars {data-filename="app/templates/scientists.hbs" data-diff="-1,-2,-3,-4,-5,-6,-7,+8,+9,+10,+11"}
280359<h2>List of Scientists</h2>
281360
@@ -289,23 +368,46 @@ Let's replace all our old code with our new componentized version:
289368 @people={{@model}}
290369/>
291370```
371+ </feature-flag-off-template-tag >
372+
373+ <feature-flag-on-template-tag >
374+ ``` gjs {data-filename="app/templates/scientists.gjs" data-diff="+2,-6,-7,-8,-9,-10,-11,+12,+13,+14,+15"}
375+ import { pageTitle } from 'ember-page-title';
376+ import PeopleList from '../components/people-list';
377+
378+ <template>
379+ {{pageTitle "Scientists"}}
380+ <h2>List of Scientists</h2>
381+ <ul>
382+ {{#each @model as |scientist|}}
383+ <li>{{scientist}}</li>
384+ {{/each}}
385+ </ul>
386+ <PeopleList
387+ @title="List of Scientists"
388+ @people={{@model}}
389+ />
390+ </template>
391+ ```
392+ </feature-flag-on-template-tag >
292393
293394Go back to your browser and you should see that the UI looks identical.
294395The only difference is that now we've componentized our list into a version that's more reusable and more maintainable.
295396
296397You can see this in action if you create a new route that shows a different list of people.
297398As an additional exercise (that we won't cover),
298399you can try to create a ` programmers ` route that shows a list of famous programmers.
299- If you re-use the ` < PeopleList> ` component, you can do it with almost no code at all.
400+ If you re-use the ` PeopleList ` component, you can do it with almost no code at all.
300401
301402## Responding to user interactions
302403
303404So far, our application is listing data, but there is no way for the user to
304405interact with the information. In web applications we often want to respond to
305406user actions like clicks or hovers. Ember makes this easy to do.
306407
307- First, we can modify the ` < PeopleList> ` component to include a button:
408+ First, we can modify the ` PeopleList ` component to include a button:
308409
410+ <feature-flag-off-template-tag >
309411``` handlebars {data-filename="app/components/people-list.hbs"}
310412<h2>{{@title}}</h2>
311413
@@ -317,16 +419,34 @@ First, we can modify the `<PeopleList>` component to include a button:
317419 {{/each}}
318420</ul>
319421```
422+ </feature-flag-off-template-tag >
423+ <feature-flag-on-template-tag >
424+ ``` gjs {data-filename="app/components/people-list.gjs"}
425+ <template>
426+ <h2>{{@title}}</h2>
427+
428+ <ul>
429+ {{#each @people as |person|}}
430+ <li>
431+ <button type="button">{{person}}</button>
432+ </li>
433+ {{/each}}
434+ </ul>
435+ </template>
436+ ```
437+ </feature-flag-on-template-tag >
438+
320439
321440Now that we have a button, we need to wire it up to do _ something_ when a user
322441clicks on it. For simplicity, let's say we want to show an ` alert ` dialog with
323442the person's name when the button is clicked.
324443
325- So far, our ` < PeopleList> ` component is purely presentational – it takes some
444+ So far, our ` PeopleList ` component is purely presentational – it takes some
326445inputs as arguments and renders them using a template. To introduce _ behavior_
327446to our component – handling the button click in this case, we will need to
328- attach some _ code _ to the component.
447+ attach some JavaScript to the component.
329448
449+ <feature-flag-off-template-tag >
330450In addition to the template, a component can also have a JavaScript file for
331451this exact purpose. Go ahead and create a ` .js ` file with the same name and in
332452the same directory as our template (` app/components/people-list.js ` ),
@@ -395,6 +515,95 @@ helper to pass the `person` as an argument which our action expects.
395515
396516Feel free to try this in the browser. Finally, everything should behave exactly
397517as we hoped!
518+ </feature-flag-off-template-tag >
519+
520+ <feature-flag-on-template-tag >
521+
522+ Let's use the [ ` on ` modifier] ( ../../components/template-lifecycle-dom-and-modifiers/#toc_event-handlers ) to handle click events on the button:
523+
524+ ``` gjs {data-filename="app/components/people-list.gjs"}
525+ import { on } from '@ember/modifier'
526+
527+ function showPerson(clickEvent) {
528+ alert(`You clicked on a button labeled ${clickEvent.target.innerHTML}`);
529+ }
530+
531+ <template>
532+ <h2>{{@title}}</h2>
533+
534+ <ul>
535+ {{#each @people as |person|}}
536+ <li>
537+ <button type="button" {{on "click" showPerson}}>{{person}}</button>
538+ </li>
539+ {{/each}}
540+ </ul>
541+ </template>
542+ ```
543+
544+ Now let's extend our example to pass the Person to our event handler as an argument. We can use the [ ` fn ` helper] ( ../../components/component-state-and-actions/#toc_passing-arguments-to-actions ) :
545+
546+ ``` gjs {data-filename="app/components/people-list.gjs"}
547+ import { on } from '@ember/modifier'
548+ import { fn } from '@ember/helper';
549+
550+ function showPerson(person) {
551+ alert(`You clicked on ${person}`);
552+ }
553+
554+ <template>
555+ <h2>{{@title}}</h2>
556+
557+ <ul>
558+ {{#each @people as |person|}}
559+ <li>
560+ <button type="button" {{on "click" (fn showPerson person) }}>{{person}}</button>
561+ </li>
562+ {{/each}}
563+ </ul>
564+ </template>
565+ ```
566+
567+ Many components will need to maintain some state. Let's introduce a ` currentPerson ` that keeps track of which Person the user clicked on last. The idiomatic way to keep state in an Ember component is to use [ ` @tracked ` ] ( ../../in-depth-topics/autotracking-in-depth/ ) on a component class:
568+
569+ ``` gjs {data-filename="app/components/people-list.gjs"}
570+ import { on } from '@ember/modifier'
571+ import { fn } from '@ember/helper';
572+ import { tracked } from '@glimmer/tracking';
573+ import Component from '@glimmer/component';
574+
575+ export default class extends Component {
576+ @tracked currentPerson;
577+
578+ showPerson = (person) => {
579+ this.currentPerson = person;
580+ };
581+
582+ isCurrentPerson = (person) => {
583+ return this.currentPerson === person;
584+ };
585+
586+ <template>
587+ <h2>{{@title}}</h2>
588+
589+ <ul>
590+ {{#each @people as |person|}}
591+ <li>
592+ <button type="button" {{on "click" (fn this.showPerson person) }}>{{person}}</button>
593+ {{#if (this.isCurrentPerson person) }}
594+ ⬅️
595+ {{/if}}
596+ </li>
597+ {{/each}}
598+ </ul>
599+ </template>
600+ }
601+ ```
602+
603+ </feature-flag-on-template-tag >
604+
605+
606+
398607
399608## Building For Production
400609
0 commit comments