@@ -135,6 +135,7 @@ ember generate route scientists
135
135
136
136
You'll see output like this:
137
137
138
+ <feature-flag-off-template-tag >
138
139
``` text
139
140
installing route
140
141
create app/routes/scientists.js
@@ -144,6 +145,20 @@ updating router
144
145
installing route-test
145
146
create tests/unit/routes/scientists-test.js
146
147
```
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 >
147
162
148
163
That is Ember telling you that it has created:
149
164
@@ -152,6 +167,7 @@ That is Ember telling you that it has created:
152
167
3 . An entry in the application's router (located in ` app/router.js ` ).
153
168
4 . A unit test for this route.
154
169
170
+ <feature-flag-off-template-tag >
155
171
Open the newly-created template in ` app/templates/scientists.hbs ` and add the following HTML:
156
172
157
173
``` 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
162
178
In your browser, open [ ` http://localhost:4200/scientists ` ] ( http://localhost:4200/scientists ) .
163
179
You should see the ` <h2> ` we put in the ` scientists.hbs ` template right below the ` <h1> ` from our ` application.hbs ` template.
164
180
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
+
165
199
Since the scientist route is nested under the application route, Ember will render its content inside the application route template's ` {{outlet}} ` directive.
166
200
167
201
Now that we've got the ` scientists ` template rendering,
@@ -191,6 +225,7 @@ the `model()` method supports any library that uses [JavaScript Promises](https:
191
225
Now let's tell Ember how to turn that array of strings into HTML.
192
226
Open the ` scientists ` template and add the following code to loop through the array and print it:
193
227
228
+ <feature-flag-off-template-tag >
194
229
``` handlebars {data-filename="app/templates/scientists.hbs"}
195
230
<h2>List of Scientists</h2>
196
231
@@ -200,6 +235,23 @@ Open the `scientists` template and add the following code to loop through the ar
200
235
{{/each}}
201
236
</ul>
202
237
```
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 >
203
255
204
256
Here, we use the ` each ` _ helper_ to loop over each item in the array we
205
257
provided 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
217
269
or using them multiple times on the same page.
218
270
Ember makes it easy to refactor your templates into reusable components.
219
271
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.
221
273
222
274
As usual, there's a generator that makes this easy for us.
223
275
Make a new component by typing:
224
276
225
277
``` 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>
226
282
ember generate component people-list
227
283
```
228
284
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:
230
287
231
288
``` handlebars {data-filename=app/components/people-list.hbs}
232
289
<h2>{{@title}}</h2>
@@ -238,6 +295,25 @@ Copy and paste the `scientists` template into the `<PeopleList>` component's tem
238
295
</ul>
239
296
```
240
297
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
+
241
317
Note that we've changed the title from a hard-coded string ("List of Scientists")
242
318
to ` {{@title}} ` . The ` @ ` indicates that ` @title ` is an argument that will be
243
319
passed 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.
246
322
We've also renamed ` scientist ` to the more-generic ` person ` ,
247
323
decreasing the coupling of our component to where it's used.
248
324
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.
250
327
251
328
<div class =" cta " >
252
329
<div class =" cta-note " >
@@ -261,6 +338,7 @@ Our component is called `<PeopleList>`, based on its name on the file system. Pl
261
338
<img src="/images/mascots/zoey.png" role="presentation" alt="">
262
339
</div >
263
340
</div >
341
+ </feature-flag-off-template-tag >
264
342
265
343
Save this template and switch back to the ` scientists ` template.
266
344
@@ -276,6 +354,7 @@ In the rest of the code examples in this tutorial, whenever we add or remove cod
276
354
277
355
Let's replace all our old code with our new componentized version:
278
356
357
+ <feature-flag-off-template-tag >
279
358
``` handlebars {data-filename="app/templates/scientists.hbs" data-diff="-1,-2,-3,-4,-5,-6,-7,+8,+9,+10,+11"}
280
359
<h2>List of Scientists</h2>
281
360
@@ -289,23 +368,46 @@ Let's replace all our old code with our new componentized version:
289
368
@people={{@model}}
290
369
/>
291
370
```
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 >
292
393
293
394
Go back to your browser and you should see that the UI looks identical.
294
395
The only difference is that now we've componentized our list into a version that's more reusable and more maintainable.
295
396
296
397
You can see this in action if you create a new route that shows a different list of people.
297
398
As an additional exercise (that we won't cover),
298
399
you 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.
300
401
301
402
## Responding to user interactions
302
403
303
404
So far, our application is listing data, but there is no way for the user to
304
405
interact with the information. In web applications we often want to respond to
305
406
user actions like clicks or hovers. Ember makes this easy to do.
306
407
307
- First, we can modify the ` < PeopleList> ` component to include a button:
408
+ First, we can modify the ` PeopleList ` component to include a button:
308
409
410
+ <feature-flag-off-template-tag >
309
411
``` handlebars {data-filename="app/components/people-list.hbs"}
310
412
<h2>{{@title}}</h2>
311
413
@@ -317,16 +419,34 @@ First, we can modify the `<PeopleList>` component to include a button:
317
419
{{/each}}
318
420
</ul>
319
421
```
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
+
320
439
321
440
Now that we have a button, we need to wire it up to do _ something_ when a user
322
441
clicks on it. For simplicity, let's say we want to show an ` alert ` dialog with
323
442
the person's name when the button is clicked.
324
443
325
- So far, our ` < PeopleList> ` component is purely presentational – it takes some
444
+ So far, our ` PeopleList ` component is purely presentational – it takes some
326
445
inputs as arguments and renders them using a template. To introduce _ behavior_
327
446
to 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.
329
448
449
+ <feature-flag-off-template-tag >
330
450
In addition to the template, a component can also have a JavaScript file for
331
451
this exact purpose. Go ahead and create a ` .js ` file with the same name and in
332
452
the 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.
395
515
396
516
Feel free to try this in the browser. Finally, everything should behave exactly
397
517
as 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
+
398
607
399
608
## Building For Production
400
609
0 commit comments