# Structural Directives # 结构型指令 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 树,以及你该如何写自己的结构型指令来完成同样的任务。 Try the . 试试。 {@a definition} ## 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 descendants. 像其它指令一样,你可以把结构型指令应用到一个*宿主元素*上。 然后它就可以对宿主元素及其子元素做点什么。 Structural directives are easy to recognize. An asterisk (*) precedes the directive attribute name as in this example. 结构型指令非常容易识别。 在这个例子中,星号(*)被放在指令的属性名之前。 No brackets. No parentheses. Just `*ngIf` set to a string. 没有方括号,没有圆括号,只是把 `*ngIf` 设置为一个字符串。 You'll learn in this guide that the [asterisk (*) is a convenience notation](guide/structural-directives#asterisk) and the string is a [_microsyntax_](guide/structural-directives#microsyntax) rather than the usual [template expression](guide/template-syntax#template-expressions). Angular desugars this notation into a marked-up `` that surrounds the host element and its descendents. Each structural directive does something different with that template. 在这个例子中,你将学到[星号(*)这个简写方法](guide/structural-directives#asterisk),而这个字符串是一个[*微语法*](guide/structural-directives#microsyntax),而不是通常的[模板表达式](guide/template-syntax#template-expressions)。 Angular 会解开这个语法糖,变成一个 `` 标记,包裹着宿主元素及其子元素。 每个结构型指令都可以用这个模板做点不同的事情。 Three of the common, built-in structural directives—[NgIf](guide/template-syntax#ngIf), [NgFor](guide/template-syntax#ngFor), and [NgSwitch...](guide/template-syntax#ngSwitch)—are described in the [_Template Syntax_](guide/template-syntax) guide and seen in samples throughout the Angular documentation. Here's an example of them in a template: 三个常用的内置结构型指令 —— [NgIf](guide/template-syntax#ngIf)、[NgFor](guide/template-syntax#ngFor)和[NgSwitch...](guide/template-syntax#ngSwitch)。 你在[*模板语法*](guide/template-syntax)一章中学过它,并且在 Angular 文档的例子中到处都在用它。下面是模板中的例子: This guide won't repeat how to _use_ them. But it does explain _how they work_ and how to [write your own](guide/structural-directives#unless) structural directive. 本章不会重复讲如何*使用*它们,而是解释它们的*工作原理*以及如何[写自己的结构型指令](guide/structural-directives#unless)。
Directive spelling
指令的拼写形式
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_. 在本章中,你将看到指令同时具有两种拼写形式*大驼峰 `UpperCamelCase` 和小驼峰 `lowerCamelCase`,比如你已经看过的 `NgIf` 和 `ngIf`。 这里的原因在于,`NgIf` 引用的是指令的*类名*,而 `ngIf` 引用的是指令的*属性名*。 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. 指令的*类名*拼写成*大驼峰形式*(`NgIf`),而它的*属性名*则拼写成*小驼峰形式*(`ngIf`)。 本章会在谈论指令的属性和工作原理时引用指令的*类名*,在描述如何在 HTML 模板中把该指令应用到元素时,引用指令的*属性名*。
There are two other kinds of Angular directives, described extensively elsewhere: (1) components and (2) attribute directives. 还有另外两种 Angular 指令,在本开发指南的其它地方有讲解:(1) 组件 (2) 属性型指令。 A *component* manages a region of HTML in the manner of a native HTML element. Technically it's a directive with a template. *组件*可以在原生 HTML 元素中管理一小片区域的 HTML。从技术角度说,它就是一个带模板的指令。 An [*attribute* directive](guide/attribute-directives) changes the appearance or behavior of an element, component, or another directive. For example, the built-in [`NgStyle`](guide/template-syntax#ngStyle) directive changes several element styles at the same time. [*属性型*指令](guide/attribute-directives)会改变某个元素、组件或其它指令的外观或行为。 比如,内置的[`NgStyle`](guide/template-syntax#ngStyle)指令可以同时修改元素的多个样式。 You can apply many _attribute_ directives to one host element. You can [only apply one](guide/structural-directives#one-per-element) _structural_ directive to a host element. 你可以在一个宿主元素上应用多个*属性型*指令,但[只能应用一个](guide/structural-directives#one-per-element)*结构型*指令。
{@a ngIf} ## 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 树出现或消失。 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` 指令并不是使用 CSS 来隐藏元素的。它会把这些元素从 DOM 中物理删除。 使用浏览器的开发者工具就可以确认这一点。
ngIf=false element not in DOM
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](guide/structural-directives#asterisk)). 可以看到第一段文字出现在了 DOM 中,而第二段则没有,在第二段的位置上是一个关于“绑定”的注释([稍后](guide/structural-directives#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. 当条件为假时,`NgIf` 会从 DOM 中移除它的宿主元素,取消它监听过的那些 DOM 事件,从 Angular 变更检测中移除该组件,并销毁它。 这些组件和 DOM 节点可以被当做垃圾收集起来,并且释放它们占用的内存。 ### Why *remove* rather than *hide*? ### 为什么*移除*而不是*隐藏*? A directive could hide the unwanted paragraph instead by setting its `display` style to `none`. 指令也可以通过把它的 `display` 风格设置为 `none` 而隐藏不需要的段落。 While invisible, the element remains in the DOM. 当不可见时,这个元素仍然留在 DOM 中。
hidden element still in DOM
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—and all of its descendant components—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—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` . 但是,除非有非常强烈的理由来保留它们,否则你会更倾向于移除用户看不见的那些 DOM 元素,并且使用 `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} ## 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. 这里的 `*ngIf` 会在 `hero` 存在时显示英雄的名字。 The asterisk is "syntactic sugar" for something a bit more complicated. Internally, Angular translates the `*ngIf` _attribute_ into a `` _element_, wrapped around the host element, like this. 星号是一个用来简化更复杂语法的“语法糖”。 从内部实现来说,Angular 把 `*ngIf` *属性* 翻译成一个 `` *元素* 并用它来包裹宿主元素,代码如下: * The `*ngIf` directive moved to the `` element where it became a property binding,`[ngIf]`. `*ngIf` 指令被移到了 `` 元素上。在那里它变成了一个属性绑定 `[ngIf]`。 * The rest of the `
`, including its class attribute, moved inside the `` element. `
` 上的其余部分,包括它的 `class` 属性在内,移到了内部的 `` 元素上。 The first form is not actually rendered, only the finished product ends up in the DOM. 第一种形态永远不会真的渲染出来。 只有最终产出的结果才会出现在 DOM 中。
hero div in DOM
Angular consumed the `` content during its actual rendering and replaced the `` with a diagnostic comment. Angular 会在真正渲染的时候填充 `` 的内容,并且把 `` 替换为一个供诊断用的注释。 The [`NgFor`](guide/structural-directives#ngFor) and [`NgSwitch...`](guide/structural-directives#ngSwitch) directives follow the same pattern. [`NgFor`](guide/structural-directives#ngFor)和[`NgSwitch...`](guide/structural-directives#ngSwitch)指令也都遵循同样的模式。 {@a ngFor} ## Inside _*ngFor_ ## `*ngFor` 内幕 Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax to `` _element_. Angular 会把 `*ngFor` 用同样的方式把星号(*)语法的 `template`*属性*转换成 ``*元素*。 Here's a full-featured application of `NgFor`, written both ways: 这里有一个 `NgFor` 的全特性应用,同时用了这三种写法: 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`). 它明显比 `ngIf` 复杂得多,确实如此。 `NgFor` 指令比本章展示过的 `NgIf` 具有更多的必选特性和可选特性。 至少 `NgFor` 会需要一个循环变量(`let hero`)和一个列表(`heroes`)。 You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](guide/structural-directives#microsyntax). 你可以通过把一个字符串赋值给 `ngFor` 来启用这些特性,这个字符串使用 Angular 的[微语法](guide/structural-directives#microsyntax)。
Everything _outside_ the `ngFor` string stays with the host element (the `
`) as it moves inside the ``. In this example, the `[ngClass]="odd"` stays on the `
`. `ngFor` 字符串*之外*的每一样东西都会留在宿主元素(`
`)上,也就是说它移到了 `` 内部。 在这个例子中,`[ngClass]="odd"` 留在了 `
` 上。
{@a microsyntax} ### Microsyntax ### 微语法 The Angular microsyntax lets you configure a directive in a compact, friendly string. The microsyntax parser translates that string into attributes on the ``: Angular 微语法能让你通过简短的、友好的字符串来配置一个指令。 微语法解析器把这个字符串翻译成 `` 上的属性: * The `let` keyword declares a [_template input variable_](guide/structural-directives#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`. `let` 关键字声明一个[模板输入变量](guide/structural-directives#template-input-variable),你会在模板中引用它。本例子中,这个输入变量就是 `hero`、`i` 和 `odd`。 解析器会把 `let hero`、`let i` 和 `let odd` 翻译成命名变量 `let-hero`、`let-i` 和 `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`. 微语法解析器接收 `of` 和 `trackby`,把它们首字母大写(`of` -> `Of`, `trackBy` -> `TrackBy`), 并且给它们加上指令的属性名(`ngFor`)前缀,最终生成的名字是 `ngForOf` 和 `ngForTrackBy`。 还有两个 `NgFor` 的*输入属性*,指令据此了解到列表是 `heroes`,而 track-by 函数是 `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`. `NgFor` 指令在列表上循环,每个循环中都会设置和重置它自己的*上下文*对象上的属性。 这些属性包括 `index` 和 `odd` 以及一个特殊的属性名 `$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. `let-i` 和 `let-odd` 变量是通过 `let i=index` 和 `let odd=odd` 来定义的。 Angular 把它们设置为*上下文*对象中的 `index` 和 `odd` 属性的当前值。 * The context property for `let-hero` wasn't specified. Its 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. 这里并没有指定 `let-hero` 的上下文属性。它的来源是隐式的。 Angular 将 `let-hero` 设置为此上下文中 `$implicit` 属性的值, 它是由 `NgFor` 用当前迭代中的英雄初始化的。 * The [API guide](api/common/NgForOf "API: NgFor") describes additional `NgFor` directive properties and context properties. [API 参考手册](api/common/NgForOf "API: NgFor")中描述了 `NgFor` 指令的其它属性和上下文属性。 * `NgFor` is implemented by the `NgForOf` directive. Read more about additional `NgForOf` directive properties and context properties [NgForOf API reference](api/common/NgForOf). `NgFor` 是由 `NgForOf` 指令来实现的。请参阅[NgForOf API reference](api/common/NgForOf)来了解 `NgForOf` 指令的更多属性及其上下文属性。 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 [`NgForOf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgForOf") is a great way to learn more. 这些微语法机制在你写自己的结构型指令时也同样有效,参考[`NgIf` 的源码](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts "Source: NgIf") 和[`NgFor` 的源码](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgFor") 可以学到更多。 {@a template-input-variable} {@a template-input-variables} ### 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`. *模板输入变量*是这样一种变量,你可以*在单个实例的模板中*引用它的值。 这个例子中有好几个模板输入变量:`hero`、`i` 和 `odd`。 它们都是用 `let` 作为前导关键字。 A _template input variable_ is **_not_** the same as a [template _reference_ variable](guide/template-syntax#ref-vars), neither _semantically_ nor _syntactically_. *模板输入变量*和[模板引用变量](guide/template-syntax#ref-vars)是**不同的**,无论是在*语义*上还是*语法*上。 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. 你使用 `let` 关键字(如 `let hero`)在模板中声明一个模板*输入*变量。 这个变量的范围被限制在所重复模板的*单一实例*上。 事实上,你可以在其它结构型指令中使用同样的变量名。 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_. 而声明模板*引用*变量使用的是给变量名加 `#` 前缀的方式(`#var`)。 一个*引用*变量引用的是它所附着到的元素、组件或指令。它可以在*整个模板*的*任意位置*访问。 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`. 模板*输入*变量和*引用*变量具有各自独立的命名空间。`let hero` 中的 `hero` 和 `#hero` 中的 `hero` 并不是同一个变量。 {@a one-per-element} ### 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. 有时你会希望只有当特定的条件为真时才重复渲染一个 HTML 块。 你可能试过把 `*ngFor` 和 `*ngIf` 放在同一个宿主元素上,但 Angular 不允许。这是因为你在一个元素上只能放一个*结构型*指令。 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? 原因很简单。结构型指令可能会对宿主元素及其子元素做很复杂的事。当两个指令放在同一个元素上时,谁先谁后?`NgIf` 优先还是 `NgFor` 优先?`NgIf` 可以取消 `NgFor` 的效果吗? 如果要这样做,Angular 应该如何把这种能力泛化,以取消其它结构型指令的效果呢? 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`](guide/structural-directives#ngcontainer) so you don't have to introduce extra levels of HTML. 对这些问题,没有办法简单回答。而禁止多个结构型指令则可以简单地解决这个问题。 这种情况下有一个简单的解决方案:把 `*ngIf` 放在一个"容器"元素上,再包装进 `*ngFor` 元素。 这个元素可以使用[`ng-container`](guide/structural-directives#ngcontainer),以免引入一个新的 HTML 层级。 {@a ngSwitch} ## Inside _NgSwitch_ directives ## `NgSwitch` 内幕 The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault`. Angular 的 `NgSwitch` 实际上是一组相互合作的指令:`NgSwitch`、`NgSwitchCase` 和 `NgSwitchDefault`。 Here's an example. 例子如下: The switch value assigned to `NgSwitch` (`hero.emotion`) determines which (if any) of the switch cases are displayed. 一个值(`hero.emotion`)被被赋值给了 `NgSwitch`,以决定要显示哪一个分支。 `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`. `NgSwitch` 本身不是结构型指令,而是一个*属性型*指令,它控制其它两个 switch 指令的行为。 这也就是为什么你要写成 `[ngSwitch]` 而不是 `*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. `NgSwitchCase` 和 `NgSwitchDefault` *都是*结构型指令。 因此你要使用星号(`*`)前缀来把它们附着到元素上。 `NgSwitchCase` 会在它的值匹配上选项值的时候显示它的宿主元素。 `NgSwitchDefault` 则会当没有兄弟 `NgSwitchCase` 匹配上时显示它的宿主元素。
The element to which you apply a directive is its _host_ element. The `` is the host element for the happy `*ngSwitchCase`. The `` is the host element for the `*ngSwitchDefault`. 指令所在的元素就是它的**宿主**元素。 `` 是 `*ngSwitchCase` 的宿主元素。 `` 是 `*ngSwitchDefault` 的宿主元素。
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault` can be desugared into the `` element form. 像其它的结构型指令一样,`NgSwitchCase` 和 `NgSwitchDefault` 也可以解开语法糖,变成 `` 的形式。 {@a prefer-asterisk} ## Prefer the asterisk (*) syntax. ## 优先使用星号(`*`)语法 The asterisk (*) syntax is more clear than the desugared form. Use [<ng-container>](guide/structural-directives#ng-container) when there's no single element to host the directive. 星号(`*`)语法比不带语法糖的形式更加清晰。 如果找不到单一的元素来应用该指令,可以使用[<ng-container>](guide/structural-directives#ng-container)作为该指令的容器。 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 `` and to understand how it works. You'll refer to the `` when you [write your own structural directive](guide/structural-directives#unless). 虽然很少有理由在模板中使用结构型指令的*属性*形式和*元素*形式,但这些幕后知识仍然是很重要的,即:Angular 会创建 ``,还要了解它的工作原理。 当需要[写自己的结构型指令](guide/structural-directives#unless)时,你就要使用 ``。 {@a template} ## The *<ng-template>* ## *<ng-template>*指令 The <ng-template> is an Angular element for rendering HTML. It is never displayed directly. In fact, before rendering the view, Angular _replaces_ the `` and its contents with a comment. <ng-template>是一个 Angular 元素,用来渲染 HTML。 它永远不会直接显示出来。 事实上,在渲染视图之前,Angular 会把 `` 及其内容*替换为*一个注释。 If there is no structural directive and you merely wrap some elements in a ``, those elements disappear. That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!". 如果没有使用结构型指令,而仅仅把一些别的元素包装进 `` 中,那些元素就是不可见的。 在下面的这个短语"Hip! Hip! Hooray!"中,中间的这个 "Hip!"(欢呼声) 就是如此。 Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic. Angular 抹掉了中间的那个 "Hip!" ,让欢呼声显得不再那么热烈了。
template tag rendering
A structural directive puts a `` to work as you'll see when you [write your own structural directive](guide/structural-directives#unless). 结构型指令会让 `` 正常工作,在你[写自己的结构型指令](guide/structural-directives#unless)时就会看到这一点。 {@a ngcontainer} {@a ng-container} ## Group sibling elements with <ng-container> ## 使用<ng-container>把一些兄弟元素归为一组 There's often a _root_ element that can and should host the structural directive. The list element (`
  • `) is a typical host element of an `NgFor` repeater. 通常都要有一个*根*元素作为结构型指令的数组。 列表元素(`
  • `)就是一个典型的供 `NgFor` 使用的宿主元素。 When there isn't a host element, you can usually wrap the content in a native HTML container element, such as a `
    `, and attach the directive to that wrapper. 当没有这样一个单一的宿主元素时,你就可以把这些内容包裹在一个原生的 HTML 容器元素中,比如 `
    `,并且把结构型指令附加到这个"包裹"上。 Introducing another container element—typically a `` or `
    `—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. 这种用于分组的元素可能会破坏模板的外观表现,因为 CSS 的样式既不曾期待也不会接受这种新的元素布局。 比如,假设你有下列分段布局。 You also have a CSS style rule that happens to apply to a `` within a `

    `aragraph. 而你的 CSS 样式规则是应用于 `

    ` 元素下的 `` 的。 The constructed paragraph renders strangely. 这样渲染出来的段落就会非常奇怪。

    spanned paragraph with bad style
    The `p span` style, intended for use elsewhere, was inadvertently applied here. 本来为其它地方准备的 `p span` 样式,被意外的应用到了这里。 Another problem: some HTML elements require all immediate children to be of a specific type. For example, the `` 元素要求直属下级必须为 `