angular-cn/public/docs/ts/latest/guide/structural-directives.jade

749 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

block includes
include ../_util-fns
//- The docs standard h4 style uppercases, making code terms unreadable. Override it.
style.
h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
:marked
This guide looks at how Angular manipulates the DOM with **structural directives** and
how you can write your own structural directives to do the same thing.
在本章中我们将看看Angular如何操纵DOM树以及我们该如何在自己的指令中这么做。
### Table of contents
### 目录
* [What are structural directives?](#definition)
[什么是结构型指令?](#definition)
* [*NgIf* case study](#ngIf)
[*NgIf* 案例](#ngIf)
* [The asterisk (*) prefix](#asterisk)
[星号(*)前缀](#asterisk)
* [Inside *NgFor*](#ngFor)
[*NgFor* 指令内幕](#ngFor)
* [microsyntax](#microsyntax)
[微语法](#microsyntax)
* [template input variables](#template-input-variable)
[模板输入变量](#template-input-variable)
* [one structural directive per element](#one-per-element)
[每个元素一个结构型指令](#one-per-element)
* [Inside the *NgSwitch* directives](#ngSwitch)
[*NgSwitch* 指令内幕](#ngSwitch)
* [Prefer the (*) prefix](#prefer-asterisk)
[优先使用(*)前缀](#prefer-asterisk)
* [The <ng-template> element](#template)
[<ng-template> 元素](#template)
* [Group sibling elements with <ng-container>](#ng-container)
[使用<ng-container>对兄弟元素进行分组](#ng-container)
* [Write a structural directive](#unless)
[写自己的结构型指令](#unless)
Try the <live-example></live-example>.
试试<live-example>在线例子</live-example>。
a#definition
.l-main-section
:marked
## What are structural directives?
## 什么是结构型指令?
Structural directives are responsible for HTML layout.
They shape or reshape the DOM's _structure_, typically by adding, removing, or manipulating
elements.
结构型指令的职责是HTML布局。
它们塑造或重塑DOM的结构比如添加、移除或维护这些元素。
As with other directives, you apply a structural directive to a _host element_.
The directive then does whatever it's supposed to do with that host element and its descendents.
像其它指令一样,你可以把结构型指令应用到一个*宿主元素*上。
然后它就可以对宿主元素及其子元素做点什么。
Structural directives are easy to recognize.
An asterisk (*) precedes the directive attribute name as in this example.
结构型指令非常容易识别。
在这个例子中,星号(*)被放在指令的属性名之前。
+makeExcerpt('src/app/app.component.html', 'ngif', '')
:marked
No brackets. No parentheses. Just `*ngIf` set to a string.
没有方括号,没有圆括号,只是把`*ngIf`设置为一个字符串。
You'll learn in this guide that the [asterisk (*) is a convenience notation](#asterisk)
and the string isa [_microsyntax_](#microsyntax) rather than the usual
[template expression](template-syntax.html#template-expressions).
Angular desugars this notation into a marked-up `<ng-template>` that surrounds the
host element and its descendents.
Each structural directive does something different with that template.
在这个例子中,我们将学到[星号(*)这个简写方法](#asterisk),而这个字符串是一个[*微语法*](#microsyntax),而不是通常的[模板表达式](template-syntax.html#template-expressions)。
Angular会解开这个语法糖变成一个`<ng-template>`标记,包裹着宿主元素及其子元素。
每个结构型指令都可以用这个模板做点不同的事情。
Three of the common, built-in structural directives&mdash;[NgIf](template-syntax.html#ngIf),
[NgFor](template-syntax.html#ngFor), and [NgSwitch...](template-syntax.html#ngSwitch)&mdash;are
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:
三个常用的内置结构型指令 —— [NgIf](template-syntax.html#ngIf)、[NgFor](template-syntax.html#ngFor)和[NgSwitch...](template-syntax.html#ngSwitch)。
我们在[*模板语法*](template-syntax.html)一章中讲过它并且在Angular文档的例子中到处都在用它。下面是模板中的例子
+makeExcerpt('src/app/app.component.html', 'built-in', '')
:marked
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.
.callout.is-helpful
header Directive spelling
:marked
Throughout this guide, you'll see a directive spelled in both _UpperCamelCase_ and _lowerCamelCase_.
Already you've seen `NgIf` and `ngIf`.
There's a reason. `NgIf` refers to the directive _class_;
`ngIf` refers to the directive's _attribute name_.
A directive _class_ is spelled in _UpperCamelCase_ (`NgIf`).
A directive's _attribute name_ is spelled in _lowerCamelCase_ (`ngIf`).
The guide refers to the directive _class_ when talking about its properties and what the directive does.
The guide refers to the _attribute name_ when describing how
you apply the directive to an element in the HTML template.
.l-sub-section
:marked
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.
Technically it's a directive with a template.
An [*attribute* directive](attribute-directives.html) changes the appearance or behavior
of an element, component, or another directive.
For example, the built-in [`NgStyle`](template-syntax.html#ngStyle) directive
changes several element styles at the same time.
You can apply many _attribute_ directives to one host element.
You can [only apply one](#one-per-element) _structural_ directive to a host element.
a#ngIf
.l-main-section
:marked
## NgIf case study
## NgIf案例分析
`NgIf` is the simplest structural directive and the easiest to understand.
It takes a boolean expression and makes an entire chunk of the DOM appear or disappear.
我们重点看下`ngIf`。它是一个很好的结构型指令案例它接受一个布尔值并据此让一整块DOM树出现或消失。
+makeExcerpt('src/app/app.component.html', 'ngif-true', '')
:marked
The `ngIf` directive doesn't hide elements with CSS. It adds and removes them physically from the DOM.
Confirm that fact using browser developer tools to inspect the DOM.
`ngIf`指令并不会隐藏元素。
使用浏览器的开发者工具就会看到:当`condition`为真的时候只剩下了DOM顶部的段落而底部无用的段落完全从DOM中消失了
在它的位置上是空白的`<script>`标签
figure.image-display
img(src='/resources/images/devguide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM")
:marked
The top paragraph is in the DOM. The bottom, disused paragraph is not;
in its place is a comment about "bindings" (more about that [later](#asterisk)).
When the condition is false, `NgIf` removes its host element from the DOM,
detaches it from DOM events (the attachments that it made),
detaches the component from Angular change detection, and destroys it.
The component and DOM nodes can be garbage-collected and free up memory.
### Why *remove* rather than *hide*?
### 为什么*移除*而不是*隐藏*
A directive could hide the unwanted paragraph instead by setting its `display` style to `none`.
指令也可以通过把它的`display`风格设置为`none`而隐藏不需要的段落。
+makeExcerpt('src/app/app.component.html', 'display-none', '')
:marked
While invisible, the element remains in the DOM.
当不可见时这个元素仍然留在DOM中。
figure.image-display
img(src='/resources/images/devguide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM")
:marked
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.
Such a component's behavior continues even when hidden.
The component stays attached to its DOM element. It keeps listening to events.
Angular keeps checking for changes that could affect data bindings.
Whatever the component was doing, it keeps doing.
对于简单的段落,隐藏和移除之间的差异影响不大,但对于资源占用较多的组件是不一样的。当我们隐藏掉一个元素时,组件的行为还在继续 —— 它仍然附加在它所属的DOM元素上
它也仍在监听事件。Angular会继续检查哪些能影响数据绑定的变更。
组件原本要做的那些事情仍在继续。
Although invisible, the component&mdash;and all of its descendant components&mdash;tie up resources.
The performance and memory burden can be substantial, responsiveness can degrade, and the user sees nothing.
虽然不可见,组件及其各级子组件仍然占用着资源,而这些资源如果分配给别人可能会更有用。
在性能和内存方面的负担相当可观,而用户却可能无法从中受益。
On the positive side, showing the element again is quick.
The component's previous state is preserved and ready to display.
The component doesn't re-initialize&mdash;an operation that could be expensive.
So hiding and showing is sometimes the right thing to do.
当然,从积极的一面看,重新显示这个元素会非常快。
组件以前的状态被保留着,并随时可以显示。
组件不用重新初始化 —— 该操作可能会比较昂贵。
But in the absence of a compelling reason to keep them around,
your preference should be to remove DOM elements that the user can't see
and recover the unused resources with a structural directive like `NgIf` .
**These same considerations apply to every structural directive, whether built-in or custom.**
Before applying a structural directive, you might want to pause for a moment
to consider the consequences of adding and removing elements and of creating and destroying components.
**同样的考量也适用于每一个结构型指令,无论是内置的还是自定义的。**
我们应该提醒自己以及我们指令的使用者,来仔细考虑添加元素、移除元素以及创建和销毁组件的后果。
a#asterisk
.l-main-section
:marked
## The asterisk (*) prefix
Surely you noticed the asterisk (*) prefix to the directive name
and wondered why it is necessary and what it does.
Here is `*ngIf` displaying the hero's name if `hero` exists.
+makeExcerpt('src/app/app.component.html', 'asterisk', '')
:marked
The asterisk is "syntactic sugar" for something a bit more complicated.
Internally, Angular desugars it in two stages.
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this.
+makeExcerpt('src/app/app.component.html', 'ngif-template-attr', '')
:marked
Then it translates the template _attribute_ into a `<ng-template>` _element_, wrapped around the host element, like this.
+makeExcerpt('src/app/app.component.html', 'ngif-template', '')
:marked
* The `*ngIf` directive moved to the `<ng-template>` element where it became a property binding,`[ngIf]`.
* The rest of the `<div>`, including its class attribute, moved inside the `<ng-template>` element.
None of these forms are actually rendered.
Only the finished product ends up in the DOM.
figure.image-display
img(src='/resources/images/devguide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM")
:marked
Angular consumed the `<ng-template>` content during its actual rendering and
replaced the `<ng-template>` with a diagnostic comment.
The [`NgFor`](#ngFor) and [`NgSwitch...`](#ngSwitch) directives follow the same pattern.
a#ngFor
.l-main-section
:marked
## Inside _*ngFor_
Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax through
template _attribute_ to `<ng-template>` _element_.
Here's a full-featured application of `NgFor`, written all three ways:
+makeExcerpt('src/app/app.component.html', 'inside-ngfor', '')
:marked
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.
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).
.alert.is-helpful
:marked
Everything _outside_ the `ngFor` string stays with the host element
(the `<div>`) as it moves inside the `<ng-template>`.
In this example, the `[ngClass]="odd"` stays on the `<div>`.
a#microsyntax
:marked
### Microsyntax
The Angular microsyntax lets you configure a directive in a compact, friendly string.
The microsyntax parser translates that string into attributes on the `<ng-template>`:
* The `let` keyword declares a [_template input variable_](#template-input-variable)
that you reference within the template. The input variables in this example are `hero`, `i`, and `odd`.
The parser translates `let hero`, `let i`, and `let odd` into variables named,
`let-hero`, `let-i`, and `let-odd`.
* The microsyntax parser takes `of` and `trackby`, title-cases them (`of` -> `Of`, `trackBy` -> `TrackBy`),
and prefixes them with the directive's attribute name (`ngFor`), yielding the names `ngForOf` and `ngForTrackBy`.
Those are the names of two `NgFor` _input properties_ .
That's how the directive learns that the list is `heroes` and the track-by function is `trackById`.
* As the `NgFor` directive loops through the list, it sets and resets properties of its own _context_ object.
These properties include `index` and `odd` and a special property named `$implicit`.
* The `let-i` and `let-odd` variables were defined as `let i=index` and `let odd=odd`.
Angular sets them to the current value of the context's `index` and `odd` properties.
* The context property for `let-hero` wasn't specified.
It's intended source is implicit.
Angular sets `let-hero` to the value of the context's `$implicit` property
which `NgFor` has initialized with the hero for the current iteration.
* The [API guide](../api/common/index/NgFor-directive.html "API: NgFor")
describes additional `NgFor` directive properties and context properties.
These microsyntax mechanisms are available to you when you write your own structural directives.
Studying the
[source code for `NgIf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts "Source: NgIf")
and [`NgFor`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgFor")
is a great way to learn more.
a#template-input-variable
a#template-input-variables
:marked
### Template input variable
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`, `i`, and `odd`.
All are preceded by the keyword `let`.
A _template input variable_ is **_not_** the same as a
[template _reference_ variable](template-syntax.html#ref-vars),
neither _semantically_ nor _syntactically_.
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.
You can use the same variable name again in the definition of other structural directives.
You declare a template _reference_ variable by prefixing the variable name with `#` (`#var`).
A _reference_ variable refers to its attached element, component or directive.
It can be accessed _anywhere_ in the _entire template_.
Template _input_ and _reference_ variable names have their own namespaces. The `hero` in `let hero` is never the same
variable as the `hero` declared as `#hero`.
a#one-per-element
:marked
### One structural directive per host element
Someday you'll want to repeat a block of HTML but only when a particular condition is true.
You'll _try_ to put both an `*ngFor` and an `*ngIf` on the same host element.
Angular won't let you. You may apply only one _structural_ directive to an element.
The reason is simplicity. Structural directives can do complex things with the host element and its descendents.
When two directives lay claim to the same host element, which one takes precedence?
Which should go first, the `NgIf` or the `NgFor`? Can the `NgIf` cancel the effect of the `NgFor`?
If so (and it seems like it should be so), how should Angular generalize the ability to cancel for other structural directives?
There are no easy answers to these questions. Prohibiting multiple structural directives makes them moot.
There's an easy solution for this use case: put the `*ngIf` on a container element that wraps the `*ngFor` element.
One or both elements can be an [`ng-container`](#ngcontainer) so you don't have to introduce extra levels of HTML.
a#ngSwitch
.l-main-section
:marked
## Inside _NgSwitch_ directives
The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault`.
Here's an example.
+makeExcerpt('src/app/app.component.html', 'ngswitch', '')
:marked
The switch value assigned to `NgSwitch` (`hero.emotion`) determines which
(if any) of the switch cases are displayed.
`NgSwitch` itself is not a structural directive.
It's an _attribute_ directive that controls the behavior of the other two switch directives.
That's why you write `[ngSwitch]`, never `*ngSwitch`.
`NgSwitchCase` and `NgSwitchDefault` _are_ structural directives.
You attach them to elements using the asterisk (*) prefix notation.
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.
.l-sub-section
:marked
*Design thought*: minimize initialization effort and consider caching state in a
companion service.
*设计思路*:要最小化初始化的成本,并考虑把状态缓存在一个伴生的服务中。
:marked
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the template _attribute_ form.
**These same considerations apply to every structural directive, whether built-in or custom.**
We should ask ourselves &mdash; and the users of our directives &mdash; to think carefully
about the consequences of adding and removing elements and of creating and destroying components.
**同样的考量也适用于每一个结构型指令,无论是内置的还是自定义的。**
我们应该提醒自己以及我们指令的使用者,来仔细考虑添加元素、移除元素以及创建和销毁组件的后果。
Let's see these dynamics at work. For fun, we'll stack the deck *against*
our recommendation and consider a component called `heavy-loader` that
***pretends*** to load a ton of data when initialized.
让我们在实践中看看这些变化。为了娱乐,我们设想在甲板上有个叫`heavy-loader`(重型起重机)的组件,它会***假装***在初始化时装载一吨数据。
We'll display two instances of the component. We toggle the visibility of the first one with CSS.
We toggle the second into and out of the DOM with `ngIf`.
我们将显示该组件的两个实例。我们使用CSS切换第一个实例的可见性用`ngIf`把第二个实例添加到DOM和将其移除。
+makeTabs(
`structural-directives/ts/src/app/structural-directives.component.html,
structural-directives/ts/src/app/heavy-loader.component.ts`,
'message-log,',
'template (excerpt), heavy-loader.component.ts')
:marked
That, in turn, can be desugared into the `<ng-template>` element form.
+makeExcerpt('src/app/app.component.html', 'ngswitch-template', '')
a#prefer-asterisk
:marked
## Prefer the asterisk (*) syntax.
The asterisk (*) syntax is more clear than the other desugared forms.
Use [&lt;ng-container&gt;](#ng-container) when there's no single element
to host the directive.
While there's rarely a good reason to apply a structural directive in template _attribute_ or _element_ form,
it's still important to know that Angular creates a `<ng-template>` and to understand how it works.
You'll refer to the `<ng-template>` when you [write your own structural directive](#unless).
a#template
.l-main-section
:marked
## The *&lt;ng-template&gt;*
The &lt;ng-template&gt; is an Angular element for rendering HTML.
It is never displayed directly.
In fact, before rendering the view, Angular _replaces_ the `<ng-template>` and its contents with a comment.
If there is no structural directive and you merely wrap some elements in a `<ng-template>`,
those elements disappear.
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
+makeExcerpt('src/app/app.component.html', 'template-tag', '')
:marked
Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
借助内置的`ngOnInit`和`ngOnDestroy`[生命周期钩子](lifecycle-hooks.html),我们同时记录了组件的创建或销毁过程。
下面是它的操作演示:
figure.image-display
img(src='/resources/images/devguide/structural-directives/template-rendering.png' width="350" alt="template tag rendering")
:marked
A structural directive puts a `<ng-template>` to work
as you'll see when you [write your own structural directive](#unless).
a#ngcontainer
a#ng-container
.l-main-section
:marked
## Group sibling elements with &lt;ng-container&gt;
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.
+makeExcerpt('src/app/app.component.html', 'ngfor-li', '')
:marked
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.
+makeExcerpt('src/app/app.component.html', 'ngif', '')
:marked
Introducing another container element&mdash;typically a `<span>` or `<div>`&mdash;to
group the elements under a single _root_ is usually harmless.
_Usually_ ... but not _always_.
The grouping element may break the template appearance because CSS styles
neither expect nor accommodate the new layout.
For example, suppose you have the following paragraph layout.
+makeExcerpt('src/app/app.component.html', 'ngif-span', '')
:marked
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
+makeExcerpt('src/app/app.component.css', 'p-span', '')
:marked
The constructed paragraph renders strangely.
figure.image-display
img(src='/resources/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style")
:marked
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.
For example, the `<select>` element requires `<option>` children.
You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
When you try this,
+makeExcerpt('src/app/app.component.html', 'select-span', '')
:marked
the drop down is empty.
figure.image-display
img(src='/resources/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work")
:marked
The browser won't display an `<option>` within a `<span>`.
### &lt;ng-container&gt; to the rescue
The Angular `<ng-container>` is a grouping element that doesn't interfere with styles or layout
because Angular _doesn't put it in the DOM_.
Here's the conditional paragraph again, this time using `<ng-container>`.
+makeExcerpt('src/app/app.component.html', 'ngif-ngcontainer', '')
:marked
It renders properly.
figure.image-display
img(src='/resources/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style")
:marked
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
+makeExcerpt('src/app/app.component.html', 'select-ngcontainer', '')
:marked
The drop down works properly.
figure.image-display
img(src='/resources/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly")
:marked
The `<ng-container>` is a syntax element recognized by the Angular parser.
It's not a directive, component, class, or interface.
It's more like the curly braces in a JavaScript `if`-block:
code-example(language="javascript").
if (someCondition) {
statement1;
statement2;
statement3;
}
:marked
Without those braces, JavaScript would only execute the first statement
when you intend to conditionally execute all of them as a single block.
The `<ng-container>` satisfies a similar need in Angular templates.
a#unless
.l-main-section
:marked
## Write a structural directive
In this section, you write an `UnlessDirective` structural directive
that does the opposite of `NgIf`.
`NgIf` displays the template content when the condition is `true`.
`UnlessDirective` displays the content when the condition is ***false***.
+makeExcerpt('src/app/app.component.html', 'myUnless-1', '')
:marked
Creating a directive is similar to creating a component.
创建指令很像创建组件。
* Import the `Directive` decorator (instead of the `Component` decorator).
导入`Directive`装饰器(而不再是`Component`)。
* 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
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" target="_blank" title="MDN: Attribute selectors">attribute selector</a>.
The directive _attribute name_ should be spelled in _lowerCamelCase_ and begin with a prefix.
Don't use `ng`. That prefix belongs to Angular.
Pick something short that fits you or your company.
In this example, the prefix is `my`.
:marked
The directive _class_ name ends in `Directive` per the [style guide](style-guide.html#02-03 "Angular Style Guide").
Angular's own directives do not.
### _TemplateRef_ and _ViewContainerRef_
A simple structural directive like this one creates an
[_embedded view_](../api/core/index/EmbeddedViewRef-class.html "API: EmbeddedViewRef")
from the Angular-generated `<ng-template>` and inserts that view in a
[_view container_](../api/core/index/ViewContainerRef-class.html "API: ViewContainerRef")
adjacent to the directive's original `<p>` host element.
You'll acquire the `<ng-template>` contents with a
[`TemplateRef`](../api/core/index/TemplateRef-class.html "API: TemplateRef")
and access the _view container_ through a
[`ViewContainerRef`](../api/core/index/ViewContainerRef-class.html "API: ViewContainerRef").
You inject both in the directive constructor as private variables of the class.
+makeExcerpt('src/app/unless.directive.ts', 'ctor', '')
:marked
### The _myUnless_ property
The directive consumer expects to bind a true/false condition to `[myUnless]`.
That means the directive needs a `myUnless` property, decorated with `@Input`
.l-sub-section
:marked
Read about `@Input` in the [_Template Syntax_](template-syntax.html#inputs-outputs) guide.
+makeExcerpt('src/app/unless.directive.ts', 'set', '')
:marked
Angular sets the `myUnless` property whenever the value of the condition changes.
Because the `myUnless` property does work, it needs a setter.
* If the condition is falsy and the view hasn't been created previously,
tell the _view container_ to create the _embedded view_ from the template.
* If the condition is truthy and the view is currently displayed,
clear the container which also destroys the view.
Nobody reads the `myUnless` property so it doesn't need a getter.
The completed directive code looks like this:
+makeExcerpt('src/app/unless.directive.ts (excerpt)', 'no-docs')
:marked
Add this directive to the `declarations` array of the AppModule.
Then create some HTML to try it.
+makeExcerpt('src/app/app.component.html', 'myUnless', '')
:marked
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.
当`condition`为`false`时,顶部的段落就会显示出来,而底部的段落消失了。
当`condition`为`true`时,顶部的段落被移除了,而底部的段落显示了出来。
figure.image-display
img(src='/resources/images/devguide/structural-directives/unless-anim.gif' alt="UnlessDirective in action" )
a#summary
.l-main-section
:marked
## Summary
You can both try and download the source code for this guide in the <live-example></live-example>.
Here is the source from the `src/app/` folder.
本章相关的代码如下:
+makeTabs(`
structural-directives/ts/src/app/app.component.ts,
structural-directives/ts/src/app/app.component.html,
structural-directives/ts/src/app/app.component.css,
structural-directives/ts/src/app/app.module.ts,
structural-directives/ts/src/app/hero.ts,
structural-directives/ts/src/app/hero-switch.components.ts,
structural-directives/ts/src/app/unless.directive.ts
`,
null,
`app.component.ts,
app.component.html,
app.component.css,
app.module.ts,
hero.ts,
hero-switch.components.ts,
unless.directive.ts
`)
:marked
You learned
* that structural directives manipulate HTML layout.
* to use [`<ng-container>`](#ngcontainer) as a grouping element when there is no suitable host element.
* that the Angular desugars [asterisk (*) syntax](#asterisk) into a `<ng-template>`.
* how that works for the `NgIf`, `NgFor` and `NgSwitch` built-in directives.
* about the [_microsyntax_](#microsyntax) that expands into a [`<ng-template>`](#template).
* to write a [custom structural directive](#unless), `UnlessDirective`.