@@ -27,15 +27,6 @@ style.
27
27
28
28
Try the <live-example></live-example>.
29
29
30
- .alert.is-helpful
31
- :marked
32
- `NgFor` and `ngFor` appear throughout the Angular documentation
33
- and you may wonder what the difference is. The _directive_ name is `NgFor`.
34
- Its _usage in a template_ is `ngFor`. This is critical in
35
- differentiating between the *context* of the directive or
36
- a *property* of the directive. `NgFor` has a `NgForTrackBy`
37
- property. The attribute `ngFor` doesn't have any properties.
38
-
39
30
a#definition
40
31
.l-main-section
41
32
:marked
@@ -49,14 +40,16 @@ a#definition
49
40
The directive then does whatever it's supposed to do with that host element and its descendents.
50
41
51
42
Structural directives are easy to recognize.
52
- An asterisk (\*) precedes the directive selector name which is set to a string .
43
+ An asterisk (\*) precedes the directive attribute name as in this example .
53
44
+ makeExample('structural-directives/ts/app/app.component.html' , 'ngif' )( format ="." )
54
45
:marked
55
- No brackets. No parentheses. Just a string, which is either a [template expression](template-syntax.html#template-expressions)
56
- or a [microsyntax](#microsyntax).
46
+ No brackets. No parentheses. Just `*ngIf` set to a string.
57
47
58
- You'll learn in this guide that the [asterisk (\*) is a convenience syntax](#asterisk). Angular "de-sugars" it into a `<template>` around the
59
- host element and its descendents. Each structural directive does something different with that template.
48
+ You'll learn in this guide that the [asterisk (\*) is a convenience notation](#asterisk)
49
+ and the string is a [_microsyntax_](#microsyntax) rather than the usual [template expression](template-syntax.html#template-expressions).
50
+ Angular "de-sugars" this notation into a marked-up `<template>` that surrounds the
51
+ host element and its descendents.
52
+ Each structural directive does something different with that template.
60
53
61
54
Three of the common, built-in structural directives—[NgIf](template-syntax.html#ngIf),
62
55
[NgFor](template-syntax.html#ngFor), and [NgSwitch...](template-syntax.html#ngSwitch)—are
@@ -68,6 +61,20 @@ a#definition
68
61
This guide won't repeat how to _use_ them. But it does explain _how they work_
69
62
and how to [write your own](#unless) structural directive.
70
63
64
+ .callout.is-helpful
65
+ header Directive spelling
66
+ :marked
67
+ Throughout this guide, you'll see a directive spelled in both _UpperCamelCase_ and _lowerCamelCase_.
68
+ Already you've seen `NgIf` and `ngIf`.
69
+ There's a reason. `NgIf` refers to the directive _class_;
70
+ `ngIf` refers to the directive's _attribute name_.
71
+
72
+ A directive _class_ is spelled in _UpperCamelCase_ (`NgIf`).
73
+ A directive's _attribute name_ is spelled in _lowerCamelCase_ (`ngIf`).
74
+ The guide refers to the directive _class_ when talking about its properties and what the directive does.
75
+ The guide refers to the _attribute name_ when describing how
76
+ you apply the directive to an element in the HTML template.
77
+
71
78
.l-sub-section
72
79
:marked
73
80
There are two other kinds of Angular directives, described extensively elsewhere: (1) components and (2) attribute directives.
@@ -83,7 +90,6 @@ a#definition
83
90
You can apply many _attribute_ directives to one host element.
84
91
You can [only apply one](#one-per-element) _structural_ directive to a host element.
85
92
86
-
87
93
a#ngIf
88
94
.l-main-section
89
95
:marked
@@ -141,8 +147,8 @@ figure.image-display
141
147
and recover the unused resources with a structural directive like `NgIf` .
142
148
143
149
**These same considerations apply to every structural directive, whether built-in or custom.**
144
- We should ask ourselves—and the users of our directives— to think carefully
145
- about the consequences of adding and removing elements and of creating and destroying components.
150
+ Before applying a structural directive, you might want to pause for a moment
151
+ to consider the consequences of adding and removing elements and of creating and destroying components.
146
152
147
153
a#ngcontainer
148
154
a#ng-container
@@ -280,12 +286,12 @@ a#ngfor
280
286
Angular transforms the `*ngFor` in similar fashion from asterisk (\*) syntax through
281
287
template _attribute_ to template _element_.
282
288
283
- Here's a full-featured `ngFor `, written all three ways:
289
+ Here's a full-featured application of `NgFor `, written all three ways:
284
290
+ makeExample('structural-directives/ts/app/app.component.html' , 'inside-ngfor' )( format ="." )
285
291
286
292
:marked
287
293
This is manifestly more complicated than `ngIf` and rightly so.
288
- The `ngFor ` directive has more features, both required and optional, than the `NgIf` shown in this guide.
294
+ The `NgFor ` directive has more features, both required and optional, than the `NgIf` shown in this guide.
289
295
At minimum `NgFor` needs a looping variable (`let hero`) and a list (`heroes`).
290
296
291
297
You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](microsyntax).
@@ -304,32 +310,30 @@ a#microsyntax
304
310
305
311
* The `let` keyword declares a [_template input variable_](#template-input-variable)
306
312
that you reference within the template. The input variables in this example are `hero`, `i`, and `odd`.
307
- The parser translates `let hero`, `let i`, and `let odd` into the declaration tokens ,
313
+ The parser translates `let hero`, `let i`, and `let odd` into variables named ,
308
314
`let-hero`, `let-i`, and `let-odd`.
309
315
310
- * The `of` and `trackby` translate to the `ngForOf` and `ngForTrackBy` _input properties_ of the `NgFor` directive.
311
- The parser creates these directive property names by title-casing the name-fragment (`of` -> `Of`, `trackBy` -> `TrackBy`) and
312
- and prefixing it with the directive key (`ngFor`).
316
+ * The microsyntax parser takes `of` and `trackby`, title-cases them (`of` -> `Of`, `trackBy` -> `TrackBy`),
317
+ and prefixes them with the directive's attribute name (`ngFor`), yielding the names `ngForOf` and `ngForTrackBy`.
318
+ Those are the names of two `NgFor` _input properties_ .
319
+ That's how the directive learns that the list is `heroes` and the track-by function is `trackById`.
313
320
314
- * The one unassigned _let_ variable (`hero`) is tied to the directive _context's_ `$implicit` property.
315
- The `NgFor` directive sets its _context's_ `$implicit` property to the current item in the list during each iteration.
316
- That becomes the current value of the `hero` variable in the example above.
321
+ * As the `NgFor` directive loops through the list, it sets and resets properties of its own _context_ object.
322
+ These properties include `index` and `odd` and a special property named `$implicit`.
317
323
318
- .alert.is-critical
319
- :marked
320
- Ward: I got lost on this third point. ^^ By context, do we mean the element that the
321
- directive is on, such as the `<div>`? Next, I was thrown by `$implicit`. What is that?
322
- Obviously a property, but why the `$` and should I know more about properties of
323
- contexts? Can we link to something for more info? I felt like this was touching
324
- on deep stuff.
325
- :marked
326
- * The other _let_ variables (`i` and `odd`) are linked to one of the directive's _named context_ properties.
327
- Look to the directive's documentation to see which properties are available and what they do.
328
- `NgFor` has several, including `index` and `odd`.
324
+ * The `let-i` and `let-odd` variables were defined as `let i=index` and `let odd=odd`.
325
+ Angular sets them to the current value of the context's `index` and `odd` properties.
326
+
327
+ * The context property for `let-hero` wasn't specified.
328
+ It's intended source is implicit.
329
+ Angular sets `let-hero` to the value of the context's `$implicit` property
330
+ which `NgFor` has initialized with the hero for the current iteration.
329
331
330
- You can leverage the microsyntax in your own structural directive, should you decide to write one as
331
- complex as `NgFor`.
332
- Studying the source code for `NgFor` is the best way to learn.
332
+ * The [API guide](../api/common/index/NgFor-directive.html "API: NgFor")
333
+ describes additional `NgFor` directive properties and context properties.
334
+
335
+ These microsyntax mechanisms are available to you when you write your own structural directives.
336
+ Studying the source code for `NgIf` and `NgFor` is a great way to learn more.
333
337
334
338
335
339
a#template-input-variable
@@ -384,26 +388,30 @@ a#ngswitch
384
388
+ makeExample('structural-directives/ts/app/app.component.html' , 'ngswitch' )( format ="." )
385
389
386
390
:marked
391
+ The switch value assigned to `NgSwitch` (`hero.emotion`) determines which
392
+ (if any) of the switch cases are displayed.
393
+
387
394
`NgSwitch` itself is not a structural directive.
388
- It's an _attribute_ directive that controls the behavior of the `NgSwitchCase` and `NgSwitchDefault` .
395
+ It's an _attribute_ directive that controls the behavior of the other two switch directives .
389
396
That's why you write `[ngSwitch]`, never `*ngSwitch`.
390
397
391
- `NgSwitchCase` and `NgSwitchDefault` are structural directives and are written with the asterisk (\*) prefix.
392
- `NgSwitchCase` includes its host element when its value matches the `NgSwitch` value.
393
- `NgSwitchDefault` includes its host element when no `NgSwitchCase` does.
398
+ `NgSwitchCase` and `NgSwitchDefault` _are_ structural directives.
399
+ You attach them to elements using the asterisk (\*) prefix notation.
400
+ An `NgSwitchCase` displays its host element when its value matches the switch value.
401
+ The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` matches the switch value.
394
402
395
- .alert.is-critical
403
+ .l-sub-section
396
404
:marked
397
- Ward: I was following along until these last two sentences above ^^. What does
398
- "includes its host element" mean? Possibly in the same way we mean when we say
399
- that something is in an include?
405
+ The element to which you apply a directive is its _host_ element.
406
+ The `<happy-hero>` is the host element for the happy `*ngSwitchCase`.
407
+ The `<unknown-hero>` is the host element for the `*ngSwitchDefault`.
400
408
401
409
:marked
402
410
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
403
411
can be "de-sugared" into the template _attribute_ form.
404
412
+ makeExample('structural-directives/ts/app/app.component.html' , 'ngswitch-template-attr' )( format ="." )
405
413
:marked
406
- The `<template>` element form is verbose .
414
+ That, in turn, can be "de-sugared" into the `<template>` element form.
407
415
+ makeExample('structural-directives/ts/app/app.component.html' , 'ngswitch-template' )( format ="." )
408
416
409
417
a#prefer-asterisk
@@ -470,20 +478,15 @@ block unless-intro
470
478
+ makeExample('structural-directives/ts/app/my-unless.directive.ts' , 'skeleton' , 'my-unless.directive.ts (skeleton)' )( format ="." )
471
479
472
480
:marked
473
- The directive's _selector_ is typically the directive's **key ** in square brackets.`[myUnless]`.
481
+ The directive's _selector_ is typically the directive's **attribute name ** in square brackets.`[myUnless]`.
474
482
The brackets define a CSS
475
483
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" target="_blank" title="MDN: Attribute selectors">attribute selector</a>.
476
484
477
- The directive _key_ should be spelled in _lowerCamelCase_ and begin with a prefix.
485
+ The directive _attribute name_ should be spelled in _lowerCamelCase_ and begin with a prefix.
478
486
Don't use `ng`. That prefix belongs to Angular.
479
487
Pick something short that fits you or your company.
480
488
In this example, the prefix is `my`.
481
489
482
- .alert.is-critical
483
- :marked
484
- Ward: Why is this called a *key* when it looks like the value in a
485
- key value pair?^^
486
-
487
490
:marked
488
491
The directive _class_ name ends in `Directive` per the [style guide](style-guide.html#02-03 "Angular Style Guide").
489
492
Angular's own directives do not.
@@ -506,33 +509,27 @@ block unless-intro
506
509
+ makeExample('structural-directives/ts/app/my-unless.directive.ts' , 'ctor' )( format ="." )
507
510
508
511
:marked
509
- The directive consumer expects to bind a condition to a directive property's `myUnless` input property.
510
- Add the `@Input() myUnless` input property as a setter-only (there's no need to read it).
512
+ ### The _myUnless_ property
511
513
512
- .alert.is-critical
514
+ The directive consumer expects to bind a true/false condition to `[myUnless]`.
515
+ That means the directive needs a `myUnless` property, decorated with `@Input`
516
+ .l-sub-section
513
517
:marked
514
- Ward: This first sentence threw me at first. I had to read it several times. I
515
- understand until the phrase "directive property's `myUnless` input property". I get
516
- "`myUnless` input property", since you just showed it, but I don't understand
517
- the relationship between the directive property...and the `@Input`? I don't
518
- know if I'm wording that correctly.
519
-
520
- For the snippet below, if I were following along to build my own, I'd have only a
521
- general notion of what was being specified in it and think to mysef "ok, I'm going to
522
- trust you on this one" and scroll down to hopefully find an example of it in action.
523
- The translation below helps but thought you should know I'd feel a little in need of
524
- bravery at first glance.
518
+ Read about `@Input` in the [_Template Syntax_](template-syntax.html#inputs-outputs) guide.
525
519
526
520
+ makeExample('structural-directives/ts/app/my-unless.directive.ts' , 'set' )( format ="." )
527
521
:marked
528
- Angular calls this setter whenever the value of the condition changes.
522
+ Angular sets the `myUnless` property whenever the value of the condition changes.
523
+ Because the `myUnless` property does work, it needs a setter.
529
524
530
525
* If the condition is falsy and the view hasn't been created previously,
531
- let the _view container_ create the _embedded view_ from the template.
526
+ tell the _view container_ to create the _embedded view_ from the template.
532
527
533
528
* If the condition is truthy and the view is currently displayed,
534
529
clear the container which also destroys the view.
535
530
531
+ Nobody reads the `myUnless` property so it doesn't need a getter.
532
+
536
533
The completed directive code looks like this:
537
534
538
535
+ makeExample('structural-directives/ts/app/my-unless.directive.ts' , 'no-docs' , 'my-unless.directive.ts' )
0 commit comments