docs(structural-directives): copyedits and some tweaks to code (#3345)

* docs(structural-directives): copyedits and some tweaks to code
- Fix variable name `li` --> `i`
- a --> an `UnlessDirective`
- Jade had both #ngswitch and #ngSwitch as link targets;
fixed to use same capitalization as template syntax page.
- Fixed broke fragment target (was missing #)
- Other copy edits and post-review comments from @wardbell
* de-sugar --> desugar
* fix h2 text capitalization
This commit is contained in:
Patrice Chalin 2017-03-09 17:30:23 -08:00 committed by Ward Bell
parent dfd360dc12
commit 7d17e342bb
4 changed files with 130 additions and 101 deletions

View File

@ -38,11 +38,11 @@
<!-- #docregion display-none --> <!-- #docregion display-none -->
<p [style.display]="'block'"> <p [style.display]="'block'">
Expression sets display to "block"" . Expression sets display to "block".
This paragraph is visible. This paragraph is visible.
</p> </p>
<p [style.display]="'none'"> <p [style.display]="'none'">
Expression sets display to "none" . Expression sets display to "none".
This paragraph is hidden but still in the DOM. This paragraph is hidden but still in the DOM.
</p> </p>
<!-- #enddocregion display-none --> <!-- #enddocregion display-none -->
@ -152,11 +152,9 @@
<div>Pick your favorite hero</div> <div>Pick your favorite hero</div>
<p> <p>
<ng-container *ngFor="let h of heroes"> <label *ngFor="let h of heroes">
<label> <input type="radio" name="heroes" [(ngModel)]="hero" [value]="h">{{h.name}}
<input type="radio" name="heroes" [(ngModel)]="hero" [value]="h">{{h.name}} </label>
</label>
</ng-container>
<label><input type="radio" name="heroes" (click)="hero = null">None of the above</label> <label><input type="radio" name="heroes" (click)="hero = null">None of the above</label>
</p> </p>

View File

@ -1,7 +1,6 @@
// #docplaster // #docplaster
// #docregion // #docregion
// #docregion no-docs // #docregion no-docs, skeleton
// #docregion skeleton
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
// #enddocregion skeleton // #enddocregion skeleton
@ -18,7 +17,7 @@ import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
* </div> * </div>
* *
* ### Syntax * ### Syntax
* * *
* - `<div *myUnless="condition">...</div>` * - `<div *myUnless="condition">...</div>`
* - `<div template="myUnless condition">...</div>` * - `<div template="myUnless condition">...</div>`
* - `<template [myUnless]="condition"><div>...</div></template>` * - `<template [myUnless]="condition"><div>...</div></template>`
@ -50,6 +49,3 @@ export class UnlessDirective {
// #enddocregion set // #enddocregion set
// #docregion skeleton // #docregion skeleton
} }
// #enddocregion skeleton
// #enddocregion no-docs
// #enddocregion

View File

@ -147,7 +147,7 @@ figure.image-display
:marked :marked
Notice the `hero` in the `ngFor` double-quoted instruction; Notice the `hero` in the `ngFor` double-quoted instruction;
it is an example of a template input variable. Read it is an example of a template input variable. Read
more about template input variables in the [microsyntax](./template-syntax.html#ngForMicrosyntax) section of more about template input variables in the [microsyntax](./template-syntax.html#microsyntax) section of
the [Template Syntax](./template-syntax.html) page. the [Template Syntax](./template-syntax.html) page.
Angular duplicates the `<li>` for each item in the list, setting the `hero` variable Angular duplicates the `<li>` for each item in the list, setting the `hero` variable

View File

@ -1,7 +1,7 @@
block includes block includes
include ../_util-fns include ../_util-fns
// The docs standard h4 style uppercases, making code terms unreadable. Override it. //- The docs standard h4 style uppercases, making code terms unreadable. Override it.
style. style.
h4 {font-size: 17px !important; text-transform: none !important;} h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; } .syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
@ -15,13 +15,13 @@ style.
- [What are structural directives?](#definition) - [What are structural directives?](#definition)
- [*NgIf* case study](#ngIf) - [*NgIf* case study](#ngIf)
- [Group sibling elements with &lt;ng-container&gt;](#ng-container) - [Group sibling elements with &lt;ng-container&gt;](#ng-container)
- [The asterisk (\*) prefix](#asterisk) - [The asterisk (*) prefix](#asterisk)
- [Inside *NgFor*](#ngfor) - [Inside *NgFor*](#ngFor)
- [microsyntax](#microsyntax) - [microsyntax](#microsyntax)
- [template input variables](#template-input-variable) - [template input variables](#template-input-variable)
- [one structural directive per element](#one-per-element) - [one structural directive per element](#one-per-element)
- [Inside the *NgSwitch* directives](#ngSwitch) - [Inside the *NgSwitch* directives](#ngSwitch)
- [Prefer the (\*) prefix](#prefer-asterisk) - [Prefer the (*) prefix](#prefer-asterisk)
- [The &lt;template> element](#template) - [The &lt;template> element](#template)
- [Write a structural directive](#unless) - [Write a structural directive](#unless)
@ -40,14 +40,17 @@ a#definition
The directive then does whatever it's supposed to do with that host element and its descendents. The directive then does whatever it's supposed to do with that host element and its descendents.
Structural directives are easy to recognize. Structural directives are easy to recognize.
An asterisk (\*) precedes the directive attribute name as in this example. An asterisk (*) precedes the directive attribute name as in this example.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngif')(format=".")
+makeExcerpt('src/app/app.component.html', 'ngif', '')
:marked :marked
No brackets. No parentheses. Just `*ngIf` set to a string. No brackets. No parentheses. Just `*ngIf` set to a string.
You'll learn in this guide that the [asterisk (\*) is a convenience notation](#asterisk) You'll learn in this guide that the [asterisk (*) is a convenience notation](#asterisk)
and the string is a [_microsyntax_](#microsyntax) rather than the usual [template expression](template-syntax.html#template-expressions). and the string is a [_microsyntax_](#microsyntax) rather than the usual
Angular "de-sugars" this notation into a marked-up `<template>` that surrounds the [template expression](template-syntax.html#template-expressions).
Angular desugars this notation into a marked-up `<template>` that surrounds the
host element and its descendents. host element and its descendents.
Each structural directive does something different with that template. Each structural directive does something different with that template.
@ -56,7 +59,8 @@ a#definition
described in the [_Template Syntax_](template-syntax.html) guide and seen in samples throughout the Angular documentation. described in the [_Template Syntax_](template-syntax.html) guide and seen in samples throughout the Angular documentation.
Here's an example of them in a template: Here's an example of them in a template:
+makeExample('structural-directives/ts/src/app/app.component.html', 'built-in')(format=".") +makeExcerpt('src/app/app.component.html', 'built-in', '')
:marked :marked
This guide won't repeat how to _use_ them. But it does explain _how they work_ This guide won't repeat how to _use_ them. But it does explain _how they work_
and how to [write your own](#unless) structural directive. and how to [write your own](#unless) structural directive.
@ -77,7 +81,8 @@ a#definition
.l-sub-section .l-sub-section
:marked :marked
There are two other kinds of Angular directives, described extensively elsewhere: (1)&nbsp;components and (2)&nbsp;attribute directives. There are two other kinds of Angular directives, described extensively elsewhere:
(1)&nbsp;components and (2)&nbsp;attribute directives.
A *component* manages a region of HTML in the manner of a native HTML element. A *component* manages a region of HTML in the manner of a native HTML element.
Technically it's a directive with a template. Technically it's a directive with a template.
@ -93,12 +98,12 @@ a#definition
a#ngIf a#ngIf
.l-main-section .l-main-section
:marked :marked
## NgIf Case Study ## NgIf case study
`NgIf` is the simplest structural directive and the easiest to understand. `NgIf` is the simplest structural directive and the easiest to understand.
It takes a boolean value and makes an entire chunk of the DOM appear or disappear. It takes a boolean expression and makes an entire chunk of the DOM appear or disappear.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngif-true')(format=".") +makeExcerpt('src/app/app.component.html', 'ngif-true', '')
:marked :marked
The `ngIf` directive doesn't hide elements with CSS. It adds and removes them physically from the DOM. The `ngIf` directive doesn't hide elements with CSS. It adds and removes them physically from the DOM.
@ -119,13 +124,15 @@ figure.image-display
### Why *remove* rather than *hide*? ### Why *remove* rather than *hide*?
A directive could hide the unwanted paragraph instead by setting its `display` style to `none`. A directive could hide the unwanted paragraph instead by setting its `display` style to `none`.
+makeExample('structural-directives/ts/src/app/app.component.html', 'display-none')(format=".")
+makeExcerpt('src/app/app.component.html', 'display-none', '')
:marked :marked
While invisible, the element remains in the DOM. While invisible, the element remains in the DOM.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM") img(src='/resources/images/devguide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM")
:marked :marked
The difference between hiding and removing doesn't matter for a simple paragraph. The difference between hiding and removing doesn't matter for a simple paragraph.
It does matter when the host element is attached to a resource intensive component. It does matter when the host element is attached to a resource intensive component.
@ -159,13 +166,13 @@ a#ng-container
There's often a _root_ element that can and should host the structural directive. There's often a _root_ element that can and should host the structural directive.
The list element (`<li>`) is a typical host element of an `NgFor` repeater. The list element (`<li>`) is a typical host element of an `NgFor` repeater.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngfor-li')(format=".") +makeExcerpt('src/app/app.component.html', 'ngfor-li', '')
:marked :marked
When there isn't a host element, you can usually wrap the content in a native HTML container element, When there isn't a host element, you can usually wrap the content in a native HTML container element,
such as a `<div>`, and attach the directive to that wrapper. such as a `<div>`, and attach the directive to that wrapper.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngif')(format=".") +makeExcerpt('src/app/app.component.html', 'ngif', '')
:marked :marked
Introducing another container element&mdash;typically a `<span>` or `<div>`&mdash;to Introducing another container element&mdash;typically a `<span>` or `<div>`&mdash;to
@ -175,27 +182,37 @@ a#ng-container
The grouping element may break the template appearance because CSS styles The grouping element may break the template appearance because CSS styles
neither expect nor accommodate the new layout. neither expect nor accommodate the new layout.
For example, suppose you have the following paragraph layout. For example, suppose you have the following paragraph layout.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngif-span')(format=".")
+makeExcerpt('src/app/app.component.html', 'ngif-span', '')
:marked :marked
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph. You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
+makeExample('structural-directives/ts/src/app/app.component.css', 'p-span')(format=".")
+makeExcerpt('src/app/app.component.css', 'p-span', '')
:marked :marked
The constructed paragraph renders strangely. The constructed paragraph renders strangely.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style") img(src='/resources/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style")
:marked :marked
The `p span` style, intended for use elsewhere, was inadvertently applied here. The `p span` style, intended for use elsewhere, was inadvertently applied here.
Another problem: some HTML elements require all immediate children to be of a specific type. Another problem: some HTML elements require all immediate children to be of a specific type.
For example, the `<select>` tag requires `<option>` children. For example, the `<select>` element requires `<option>` children.
You can't wrap the _options_ in a conditional `<div>` or a `<span>`. You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
When you try this, When you try this,
+makeExample('structural-directives/ts/src/app/app.component.html', 'select-span')(format=".")
+makeExcerpt('src/app/app.component.html', 'select-span', '')
:marked :marked
the drop down is empty. the drop down is empty.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work") img(src='/resources/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work")
:marked :marked
The browser won't display an `<option>` within a `<span>`. The browser won't display an `<option>` within a `<span>`.
@ -205,16 +222,23 @@ figure.image-display
because Angular _doesn't put it in the DOM_. because Angular _doesn't put it in the DOM_.
Here's the conditional paragraph again, this time using `<ng-container>`. Here's the conditional paragraph again, this time using `<ng-container>`.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngif-ngcontainer')(format=".")
+makeExcerpt('src/app/app.component.html', 'ngif-ngcontainer', '')
:marked :marked
It renders properly. It renders properly.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style") img(src='/resources/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style")
:marked :marked
Now conditionally exclude a _select_ `<option>` with `<ng-container>`. Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
+makeExample('structural-directives/ts/src/app/app.component.html', 'select-ngcontainer')(format=".")
+makeExcerpt('src/app/app.component.html', 'select-ngcontainer', '')
:marked :marked
The drop down works properly. The drop down works properly.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly") img(src='/resources/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly")
@ -229,46 +253,43 @@ code-example(language="javascript").
statement2; statement2;
statement3; statement3;
} }
:marked :marked
Without those braces JavaScript could only execute the first statement Without those braces, JavaScript would only execute the first statement
when you intend to conditionally execute all of them as a single block. when you intend to conditionally execute all of them as a single block.
The `<ng-container>` satisfies a similar need in Angular templates. The `<ng-container>` satisfies a similar need in Angular templates.
a#asterisk a#asterisk
.l-main-section .l-main-section
:marked :marked
## The asterisk (\*) prefix ## The asterisk (*) prefix
Surely you noticed the asterisk (\*) prefix to the directive name Surely you noticed the asterisk (*) prefix to the directive name
and wondered why it is necessary and what it does. and wondered why it is necessary and what it does.
Here is `*ngIf` displaying the hero's name if `hero` exists. Here is `*ngIf` displaying the hero's name if `hero` exists.
+makeExample('structural-directives/ts/src/app/app.component.html', 'asterisk')(format=".") +makeExcerpt('src/app/app.component.html', 'asterisk', '')
:marked :marked
The asterisk is "syntactic sugar" for something a bit more complicated. The asterisk is "syntactic sugar" for something a bit more complicated.
Internally, Angular "de-sugars" it in two stages. Internally, Angular "desugars" it in two stages.
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this. First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngif-template-attr')(format=".")
+makeExcerpt('src/app/app.component.html', 'ngif-template-attr', '')
:marked :marked
Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this. Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngif-template')(format=".")
// We should discourage writing <template> syntax in favor of <ng-container> +makeExcerpt('src/app/app.component.html', 'ngif-template', '')
block remember-the-brackets
.alert.is-critical
:marked
Write `[ngIf]="hero"`, not `ngIf="hero"`!
The latter incorrectly assigns the *string* `"hero"` to `ngIf`.
A non-empty string is _truthy_, so `ngIf` is always `true` and Angular
always tries to show the content … even when there is no `hero`.
:marked :marked
* The `*ngIf` directive moved to the `<template>` tag where it became a property binding,`[ngIf]`. * The `*ngIf` directive moved to the `<template>` element where it became a property binding,`[ngIf]`.
* The rest of the `<div>`, including its class attribute, moved inside the `<template>` tag. * The rest of the `<div>`, including its class attribute, moved inside the `<template>` element.
None of these forms are actually rendered. None of these forms are actually rendered.
Only the finished product ends up in the DOM. Only the finished product ends up in the DOM.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM") img(src='/resources/images/devguide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM")
@ -276,25 +297,26 @@ figure.image-display
Angular consumed the `<template>` content during its actual rendering and Angular consumed the `<template>` content during its actual rendering and
replaced the `<template>` with a diagnostic comment. replaced the `<template>` with a diagnostic comment.
The [`NgFor`](#ngfor) and [`NgSwitch...`](#ngswitch) directives follow the same pattern. The [`NgFor`](#ngFor) and [`NgSwitch...`](#ngSwitch) directives follow the same pattern.
a#ngfor a#ngfor
.l-main-section .l-main-section
:marked :marked
## Inside _*ngFor_ ## Inside _*ngFor_
Angular transforms the `*ngFor` in similar fashion from asterisk (\*) syntax through Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax through
template _attribute_ to template _element_. template _attribute_ to template _element_.
Here's a full-featured application of `NgFor`, written all three ways: Here's a full-featured application of `NgFor`, written all three ways:
+makeExample('structural-directives/ts/src/app/app.component.html', 'inside-ngfor')(format=".")
+makeExcerpt('src/app/app.component.html', 'inside-ngfor', '')
:marked :marked
This is manifestly more complicated than `ngIf` and rightly so. This is manifestly more complicated than `ngIf` and rightly so.
The `NgFor` directive has more features, both required and optional, than the `NgIf` shown in this guide. The `NgFor` directive has more features, both required and optional, than the `NgIf` shown in this guide.
At minimum `NgFor` needs a looping variable (`let hero`) and a list (`heroes`). At minimum `NgFor` needs a looping variable (`let hero`) and a list (`heroes`).
You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](microsyntax). You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](#microsyntax).
.alert.is-helpful .alert.is-helpful
:marked :marked
@ -304,7 +326,8 @@ a#ngfor
a#microsyntax a#microsyntax
:marked :marked
### microsyntax ### Microsyntax
The Angular microsyntax lets you configure a directive in a compact, friendly string. The Angular microsyntax lets you configure a directive in a compact, friendly string.
The microsyntax parser translates that string into attributes on the `<template>`: The microsyntax parser translates that string into attributes on the `<template>`:
@ -342,18 +365,18 @@ a#template-input-variables
### Template input variable ### Template input variable
A _template input variable_ is a variable whose value you can reference _within_ a single instance of the template. A _template input variable_ is a variable whose value you can reference _within_ a single instance of the template.
There are several such variables in this example: `hero`, `li`, and `odd`. There are several such variables in this example: `hero`, `i`, and `odd`.
All are preceded by the keyword `let`. All are preceded by the keyword `let`.
A _template input variable_ is **_not_** the same as a A _template input variable_ is **_not_** the same as a
[template _reference_ variable](template-syntax.html#ref-vars), [template _reference_ variable](template-syntax.html#ref-vars),
neither _semantically_ nor _syntactically_. neither _semantically_ nor _syntactically_.
You declare a template _input_ variable declaration with the `let` keyword (`let hero`). You declare a template _input_ variable using the `let` keyword (`let hero`).
The variable's scope is limited to a _single instance_ of the repeated template. The variable's scope is limited to a _single instance_ of the repeated template.
You can use the same variable name again in the definition of other structural directives. You can use the same variable name again in the definition of other structural directives.
You declare a template _reference_ variable declaration by prefixing the variable name with `#` (`#var`). You declare a template _reference_ variable by prefixing the variable name with `#` (`#var`).
A _reference_ variable refers to its attached element, component or directive. A _reference_ variable refers to its attached element, component or directive.
It can be accessed _anywhere_ in the _entire template_. It can be accessed _anywhere_ in the _entire template_.
@ -380,12 +403,13 @@ a#one-per-element
a#ngswitch a#ngswitch
.l-main-section .l-main-section
:marked :marked
## Inside the _NgSwitch_ directives ## Inside _NgSwitch_ directives
The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault`. The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault`.
Here's an example. Here's an example.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngswitch')(format=".")
+makeExcerpt('src/app/app.component.html', 'ngswitch', '')
:marked :marked
The switch value assigned to `NgSwitch` (`hero.emotion`) determines which The switch value assigned to `NgSwitch` (`hero.emotion`) determines which
@ -396,7 +420,7 @@ a#ngswitch
That's why you write `[ngSwitch]`, never `*ngSwitch`. That's why you write `[ngSwitch]`, never `*ngSwitch`.
`NgSwitchCase` and `NgSwitchDefault` _are_ structural directives. `NgSwitchCase` and `NgSwitchDefault` _are_ structural directives.
You attach them to elements using the asterisk (\*) prefix notation. You attach them to elements using the asterisk (*) prefix notation.
An `NgSwitchCase` displays its host element when its value matches the switch value. An `NgSwitchCase` displays its host element when its value matches the switch value.
The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` matches the switch value. The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` matches the switch value.
@ -408,17 +432,20 @@ a#ngswitch
:marked :marked
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault` As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be "de-sugared" into the template _attribute_ form. can be desugared into the template _attribute_ form.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngswitch-template-attr')(format=".")
+makeExcerpt('src/app/app.component.html', 'ngswitch-template-attr', '')
:marked :marked
That, in turn, can be "de-sugared" into the `<template>` element form. That, in turn, can be desugared into the `<template>` element form.
+makeExample('structural-directives/ts/src/app/app.component.html', 'ngswitch-template')(format=".")
+makeExcerpt('src/app/app.component.html', 'ngswitch-template', '')
a#prefer-asterisk a#prefer-asterisk
:marked :marked
## Prefer the asterisk (\*) syntax. ## Prefer the asterisk (*) syntax.
The asterisk (\*) syntax is more clear than the other "de-sugared" forms. The asterisk (*) syntax is more clear than the other desugared forms.
Use [&lt;ng-container&gt;](#ng-container) when there's no single element Use [&lt;ng-container&gt;](#ng-container) when there's no single element
to host the directive. to host the directive.
@ -436,12 +463,15 @@ a#template
It is never displayed directly. It is never displayed directly.
In fact, before rendering the view, Angular _replaces_ the `<template>` and its contents with a comment. In fact, before rendering the view, Angular _replaces_ the `<template>` and its contents with a comment.
If there is no structural directive, if you merely wrap some elements in a `<template>` and do nothing with it, If there is no structural directive and you merely wrap some elements in a `<template>`,
those elements disappear. those elements disappear.
That's the fate of the middle "hip" in the phrase "Hip! Hip! Hooray!". That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
+makeExample('structural-directives/ts/src/app/app.component.html', 'template-tag')(format=".")
+makeExcerpt('src/app/app.component.html', 'template-tag', '')
:marked :marked
Angular erases the middle "hip", leaving the cheer a bit less enthusiastic. Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/template-rendering.png' width="350" alt="template tag rendering") img(src='/resources/images/devguide/structural-directives/template-rendering.png' width="350" alt="template tag rendering")
@ -453,32 +483,31 @@ a#unless
.l-main-section .l-main-section
:marked :marked
## Write a structural directive ## Write a structural directive
In this section, you write a `UnlessDirective` structural directive
In this section, you write an `UnlessDirective` structural directive
that does the opposite of `NgIf`. that does the opposite of `NgIf`.
`NgIf` displays the template content when the condition is `true`. `NgIf` displays the template content when the condition is `true`.
`UnlessDirective` displays the content when the condition is ***false***. `UnlessDirective` displays the content when the condition is ***false***.
+makeExample('structural-directives/ts/src/app/app.component.html', 'myUnless-1')(format=".") +makeExcerpt('src/app/app.component.html', 'myUnless-1', '')
:marked
block unless-intro
:marked
Creating a directive is similar to creating a component.
* Import the `Directive` decorator (instead of the `Component` decorator).
* Import the `Input`, `TemplateRef`, and `ViewContainerRef` symbols; you'll need them for _any_ structural directive.
* Apply the decorator to the directive class.
* Set the CSS *attribute selector* that identifies the directive when applied to an element in a template.
Here's how you might begin:
+makeExample('structural-directives/ts/src/app/unless.directive.ts', 'skeleton', 'unless.directive.ts (skeleton)')(format=".")
:marked :marked
The directive's _selector_ is typically the directive's **attribute name** in square brackets.`[myUnless]`. Creating a directive is similar to creating a component.
* Import the `Directive` decorator (instead of the `Component` decorator).
* Import the `Input`, `TemplateRef`, and `ViewContainerRef` symbols; you'll need them for _any_ structural directive.
* Apply the decorator to the directive class.
* Set the CSS *attribute selector* that identifies the directive when applied to an element in a template.
Here's how you might begin:
+makeExcerpt('src/app/unless.directive.ts (skeleton)')
:marked
The directive's _selector_ is typically the directive's **attribute name** in square brackets, `[myUnless]`.
The brackets define a CSS The brackets define a CSS
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" target="_blank" title="MDN: Attribute selectors">attribute selector</a>. <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" target="_blank" title="MDN: Attribute selectors">attribute selector</a>.
@ -506,18 +535,20 @@ block unless-intro
You inject both in the directive constructor as private variables of the class. You inject both in the directive constructor as private variables of the class.
+makeExample('structural-directives/ts/src/app/unless.directive.ts', 'ctor')(format=".") +makeExcerpt('src/app/unless.directive.ts', 'ctor', '')
:marked :marked
### The _myUnless_ property ### The _myUnless_ property
The directive consumer expects to bind a true/false condition to `[myUnless]`. The directive consumer expects to bind a true/false condition to `[myUnless]`.
That means the directive needs a `myUnless` property, decorated with `@Input` That means the directive needs a `myUnless` property, decorated with `@Input`
.l-sub-section .l-sub-section
:marked :marked
Read about `@Input` in the [_Template Syntax_](template-syntax.html#inputs-outputs) guide. Read about `@Input` in the [_Template Syntax_](template-syntax.html#inputs-outputs) guide.
+makeExample('structural-directives/ts/src/app/unless.directive.ts', 'set')(format=".") +makeExcerpt('src/app/unless.directive.ts', 'set', '')
:marked :marked
Angular sets the `myUnless` property whenever the value of the condition changes. Angular sets the `myUnless` property whenever the value of the condition changes.
Because the `myUnless` property does work, it needs a setter. Because the `myUnless` property does work, it needs a setter.
@ -532,16 +563,19 @@ block unless-intro
The completed directive code looks like this: The completed directive code looks like this:
+makeExample('structural-directives/ts/src/app/unless.directive.ts', 'no-docs', 'unless.directive.ts') +makeExcerpt('src/app/unless.directive.ts (excerpt)', 'no-docs')
:marked :marked
Add this directive to the `!{_declsVsDirectives}` !{_array} of the !{_AppModuleVsAppComp}. Add this directive to the `!{_declsVsDirectives}` !{_array} of the !{_AppModuleVsAppComp}.
Then create some HTML to try it. Then create some HTML to try it.
+makeExample('structural-directives/ts/src/app/app.component.html', 'myUnless')(format=".")
+makeExcerpt('src/app/app.component.html', 'myUnless', '')
:marked :marked
When the `condition` is falsy, the top (A) paragraph appears and the bottom (B) paragraph disappears. When the `condition` is falsy, the top (A) paragraph appears and the bottom (B) paragraph disappears.
When the `condition` is truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears. When the `condition` is truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears.
figure.image-display figure.image-display
img(src='/resources/images/devguide/structural-directives/unless-anim.gif' alt="UnlessDirective in action" ) img(src='/resources/images/devguide/structural-directives/unless-anim.gif' alt="UnlessDirective in action" )
@ -551,7 +585,7 @@ a#summary
## Summary ## Summary
You can both try and download the source code for this guide in the <live-example></live-example>. You can both try and download the source code for this guide in the <live-example></live-example>.
Here is the source from the `app/` folder. Here is the source from the `src/app/` folder.
+makeTabs(` +makeTabs(`
structural-directives/ts/src/app/app.component.ts, structural-directives/ts/src/app/app.component.ts,
@ -574,9 +608,10 @@ a#summary
:marked :marked
You learned You learned
* that structural directives manipulate HTML layout. * that structural directives manipulate HTML layout.
* to use [`<ng-container>`](#ngcontainer) as a grouping element when there is no suitable host element. * to use [`<ng-container>`](#ngcontainer) as a grouping element when there is no suitable host element.
* that the angular "de-sugars" [asterisk (\*) syntax](#asterisk) into a `<template>`. * that the Angular "desugars" [asterisk (*) syntax](#asterisk) into a `<template>`.
* how that works for the `NgIf`, `NgFor` and `NgSwitch` built-in directives. * how that works for the `NgIf`, `NgFor` and `NgSwitch` built-in directives.
* about the [_microsyntax_](#microsyntax) that expands into a [`<template>`](#template). * about the [_microsyntax_](#microsyntax) that expands into a [`<template>`](#template).
* to write a [custom structural directive](#unless), `UnlessDirective`. * to write a [custom structural directive](#unless), `UnlessDirective`.