fix: 把除了 upgrading 之外的 我们 都替换成了 你
fix: 翻译了一部分 AOT Compiler
This commit is contained in:
parent
5ff85f455c
commit
f75baea397
|
@ -78,7 +78,7 @@ The following table lists some of the key AngularJS template features with their
|
|||
the binding is prefixed with the controller alias (`vm` or `$ctrl`) because you
|
||||
have to be specific about the source of the binding.
|
||||
|
||||
当使用 `controller as` 语法时,该绑定需要用控制器的别名(`vm`)为前缀,这是因为我们不得不通过它来指定绑定源。
|
||||
当使用 `controller as` 语法时,该绑定需要用控制器的别名(`vm`)为前缀,这是因为你不得不通过它来指定绑定源。
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -145,7 +145,7 @@ The following table lists some of the key AngularJS template features with their
|
|||
Many (but not all) of the built-in filters from AngularJS are
|
||||
built-in pipes in Angular.
|
||||
|
||||
在 Angular 中,我们使用相似的语法 —— 用管道字符(|)来过滤输出,但是现在直接把它叫做**管道**了。
|
||||
在 Angular 中,你使用类似的语法 —— 用管道字符(|)来过滤输出,但是现在直接把它叫做**管道**了。
|
||||
很多(但不是所有)AngularJS 中的内置过滤器也成了 Angular 中的内置管道。
|
||||
|
||||
For more information, see [Filters/pipes](guide/ajs-quick-reference#filters-pipes) below.
|
||||
|
@ -545,7 +545,7 @@ AngularJS 为模板提供了七十多个内置指令。
|
|||
Place the element's `href` property in square brackets and set it to a quoted template expression.
|
||||
|
||||
在 Angular 中,并没有内置的 *href* 指令,改用属性绑定。
|
||||
我们把元素的 `href` 属性放在方括号中,并把它设成一个引号中的模板表达式。
|
||||
把元素的 `href` 属性放在方括号中,并把它设成一个引号中的模板表达式。
|
||||
|
||||
For more information see the [Property binding](guide/template-syntax#property-binding)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
|
@ -646,7 +646,7 @@ AngularJS 为模板提供了七十多个内置指令。
|
|||
and event binding (from the view to the component), thereby providing two-way binding.
|
||||
|
||||
在 Angular 中,**双向绑定**使用[()]标记出来,它被形象的比作“盒子中的香蕉”。
|
||||
这种语法是一个简写形式,用来同时定义一个属性绑定(从组件到视图)和一个事件绑定(从视图到组件),因此,我们得到了双向绑定。
|
||||
这种语法是一个简写形式,用来同时定义一个属性绑定(从组件到视图)和一个事件绑定(从视图到组件),就成了双向绑定。
|
||||
|
||||
For more information on two-way binding with `ngModel`, see the [NgModel—Two-way binding to
|
||||
form elements with `[(ngModel)]`](../guide/template-syntax.html#ngModel)
|
||||
|
@ -928,11 +928,11 @@ AngularJS 为模板提供了七十多个内置指令。
|
|||
If that method returns `true`, the app selects `*ngSwitchCase="true"` and displays: "Excellent choice!"
|
||||
If that methods returns `false`, the app selects `*ngSwitchCase="false"` and displays: "No movie, sorry!"
|
||||
|
||||
在这个例子中,如果 `favoriteHero` 没有设置,则 `ngSwitch` 的值是 `null`,我们会看到
|
||||
`*ngSwitchDefault` 中的段落“Please enter ...”。
|
||||
如果 `favoriteHero` 被设置了,它就会通过调用一个组件方法来检查电影英雄。
|
||||
如果该方法返回 `true`,我们就会看到“Excellent choice!”。
|
||||
如果该方法返回 `false`,我们就会看到“No movie, sorry!”。
|
||||
在这个例子中,如果 `favoriteHero` 没有设置,则 `ngSwitch` 的值是 `null`,
|
||||
`*ngSwitchDefault` 中会显示 “Please enter ...”。
|
||||
如果设置了 `favoriteHero`,应用就会通过调用一个组件方法来检查电影英雄。
|
||||
如果该方法返回 `true`,就会显示 “Excellent choice!”。
|
||||
如果该方法返回 `false`,就会显示 “No movie, sorry!”。
|
||||
|
||||
The (*) before `ngSwitchCase` and `ngSwitchDefault` is required in this example.
|
||||
|
||||
|
@ -1276,13 +1276,13 @@ AngularJS 中的很多内置过滤器在 Angular 中都有对应的管道。
|
|||
|
||||
In both AngularJS and Angular, modules help you organize your application into cohesive blocks of functionality.
|
||||
|
||||
无论在 AngularJS 还是 Angular 中,我们都要借助“模块”来把应用拆分成一些紧密相关的功能块。
|
||||
无论在 AngularJS 还是 Angular 中,你都要借助“模块”来把应用拆分成一些紧密相关的功能块。
|
||||
|
||||
In AngularJS, you write the code that provides the model and the methods for the view in a **controller**.
|
||||
In Angular, you build a **component**.
|
||||
|
||||
在 AngularJS 中,我们在**控制器**中写代码,来为视图提供模型和方法。
|
||||
在 Angular 中,我们创建**组件**。
|
||||
在 AngularJS 中,你要在**控制器**中写代码,来为视图提供模型和方法。
|
||||
在 Angular 中,你要创建**组件**。
|
||||
|
||||
Because much AngularJS code is in JavaScript, JavaScript code is shown in the AngularJS column.
|
||||
The Angular code is shown using TypeScript.
|
||||
|
@ -1345,7 +1345,7 @@ The Angular code is shown using TypeScript.
|
|||
This is a nonissue in Angular because ES 2015 modules
|
||||
handle the namespacing for you.
|
||||
|
||||
在 Angular 中我们不用担心这个问题,因为使用 ES 2015 的模块,模块会替我们处理命名空间问题。
|
||||
在 Angular 中不用担心这个问题,因为使用 ES 2015 的模块,模块会替你处理命名空间问题。
|
||||
|
||||
For more information on modules, see the [Modules](guide/architecture#modules) section of the
|
||||
[Architecture Overview](guide/architecture).
|
||||
|
@ -1445,7 +1445,7 @@ The Angular code is shown using TypeScript.
|
|||
The `@Component` decorator declares that the class is a component and provides metadata about
|
||||
that component such as its selector (or tag) and its template.
|
||||
|
||||
在 Angular 中,我们往组件类上添加了一个装饰器,以提供任何需要的元数据。
|
||||
Angular 会往组件类上添加了一个装饰器,以提供所需的任何元数据。
|
||||
`@Component` 装饰器把该类声明为组件,并提供了关于该组件的元数据,比如它的选择器(或标签)和模板。
|
||||
|
||||
This is how you associate a template with logic, which is defined in the component class.
|
||||
|
@ -1478,7 +1478,7 @@ The Angular code is shown using TypeScript.
|
|||
|
||||
In AngularJS, you write the code for the model and methods in a controller function.
|
||||
|
||||
在 Angular1 中,我们在控制器函数中写模型和方法的代码。
|
||||
在 Angular1 中,你在控制器函数中编写模型和方法的代码。
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -1492,7 +1492,7 @@ The Angular code is shown using TypeScript.
|
|||
|
||||
In Angular, you create a component class.
|
||||
|
||||
在 Angular 中,我们写组件类。
|
||||
在 Angular 中,你要创建组件类。
|
||||
|
||||
NOTE: If you are using TypeScript with AngularJS, you must use the `export` keyword to export the component class.
|
||||
|
||||
|
@ -1526,13 +1526,13 @@ The Angular code is shown using TypeScript.
|
|||
In AngularJS, you pass in any dependencies as controller function arguments.
|
||||
This example injects a `MovieService`.
|
||||
|
||||
在 AngularJS 中,我们把所有依赖都作为控制器函数的参数。
|
||||
在这个例子中,我们注入了一个 `MovieService`。
|
||||
在 AngularJS 中,你把所有依赖都作为控制器函数的参数。
|
||||
这个例子注入了一个 `MovieService`。
|
||||
|
||||
To guard against minification problems, tell Angular explicitly
|
||||
that it should inject an instance of the `MovieService` in the first parameter.
|
||||
|
||||
我们还通过在第一个参数明确告诉 Angular 它应该注入一个 `MovieService` 的实例,以防止在最小化时出现问题。
|
||||
为了防止在最小化时出现问题,第一个参数明确告诉 Angular 它应该注入一个 `MovieService` 的实例。
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -1548,8 +1548,8 @@ The Angular code is shown using TypeScript.
|
|||
This example injects a `MovieService`.
|
||||
The first parameter's TypeScript type tells Angular what to inject, even after minification.
|
||||
|
||||
在 Angular 中,我们把依赖作为组件构造函数的参数传入。
|
||||
在这个例子中,我们注入了一个 `MovieService`。
|
||||
在 Angular 中,你要把依赖作为组件构造函数的参数传入。
|
||||
这个例子注入了一个 `MovieService`。
|
||||
即使在最小化之后,第一个参数的 TypeScript 类型也会告诉 Angular 它该注入什么。
|
||||
|
||||
For more information, see the [Dependency injection](guide/architecture#dependency-injection)
|
||||
|
@ -1576,10 +1576,10 @@ merge, which can cause unexpected results.
|
|||
In Angular, you can still define style sheets for your entire application. But now you can
|
||||
also encapsulate a style sheet within a specific component.
|
||||
|
||||
样式表美化我们的应用程序。
|
||||
在 AngularJS 中,我们为整个应用程序指定样式表。
|
||||
当应用程序成长一段时间之后,应用程序中很多部分的样式会被合并,导致无法预计的后果。
|
||||
在 Angular 中,我们仍然会为整个应用程序定义样式,不过现在也可以把样式表封装在特定的组件中。
|
||||
样式表让你的应用程序看起来更漂亮。
|
||||
在 AngularJS 中,你要为整个应用程序指定样式表。
|
||||
随着应用程序的不断成长,为各个部分指定的样式会被合并,导致无法预计的后果。
|
||||
在 Angular 中,你仍然要为整个应用程序定义样式,不过现在也可以把样式表封装在特定的组件中。
|
||||
|
||||
<table width="100%">
|
||||
|
||||
|
@ -1624,7 +1624,7 @@ also encapsulate a style sheet within a specific component.
|
|||
AngularJS, uses a `link` tag in the head section of the `index.html` file
|
||||
to define the styles for the application.
|
||||
|
||||
在 AngularJS 中,我们在 `index.html` 的 `head` 区使用 `link` 标签来为应用程序定义样式。
|
||||
AngularJS 在 `index.html` 的 `head` 区使用 `link` 标签来为应用程序定义样式。
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -1639,7 +1639,7 @@ also encapsulate a style sheet within a specific component.
|
|||
With the Angular CLI, you can configure your global styles in the `.angular-cli.json` file.
|
||||
You can rename the extension to `.scss` to use sass.
|
||||
|
||||
使用 Angular CLI,我们可以在 `.angular-cli.json` 文件中配置全局样式。
|
||||
使用 Angular CLI,你可以在 `.angular-cli.json` 文件中配置全局样式。
|
||||
也可以把扩展名改为 `.scss` 来使用 sass。
|
||||
|
||||
### StyleUrls
|
||||
|
@ -1647,14 +1647,14 @@ also encapsulate a style sheet within a specific component.
|
|||
In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define
|
||||
a style sheet for a particular component.
|
||||
|
||||
在 Angular 中,我们可以在 `@Component` 的元数据中使用 `styles` 或 `styleUrls` 属性来为一个特定的组件定义样式表。
|
||||
在 Angular 中,你可以在 `@Component` 的元数据中使用 `styles` 或 `styleUrls` 属性来为一个特定的组件定义样式表。
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url" linenums="false"></code-example>
|
||||
|
||||
This allows you to set appropriate styles for individual components that won’t leak into
|
||||
other parts of the application.
|
||||
|
||||
这让我们可以为各个组件设置合适的样式,而不用担心它被泄漏到程序中的其它部分。
|
||||
这让你可以为各个组件设置合适的样式,而不用担心它被泄漏到程序中的其它部分。
|
||||
|
||||
</td>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ user interfaces transition smoothly between states with engaging animations
|
|||
that call attention where it's needed. Well-designed animations can make a UI not only
|
||||
more fun but also easier to use.
|
||||
|
||||
动画是现代 Web 应用设计中一个很重要的方面。我们希望用户界面能在不同的状态之间更平滑的转场。如果需要,还可以用适当的动画来吸引注意力。
|
||||
动画是现代 Web 应用设计中一个很重要的方面。好的用户界面要能在不同的状态之间更平滑的转场。如果需要,还可以用适当的动画来吸引注意力。
|
||||
设计良好的动画不但会让 UI 更有趣,还会让它更容易使用。
|
||||
|
||||
## Overview
|
||||
|
@ -19,7 +19,7 @@ performance found in pure CSS animations. You can also tightly integrate your
|
|||
animation logic with the rest of your application code, for ease of control.
|
||||
|
||||
Angular 的动画系统赋予了制作各种动画效果的能力,以构建出与原生 CSS 动画性能相同的动画。
|
||||
我们也获得了额外的让动画逻辑与其它应用代码紧紧集成在一起的能力,这让动画可以被更容易的触发与控制。
|
||||
你还获得了额外的让动画逻辑与其它应用代码紧紧集成在一起的能力,这让动画可以被更容易的触发与控制。
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
@ -90,7 +90,7 @@ The buttons trigger changes to the list that all of the example components see a
|
|||
You can build a simple animation that transitions an element between two states
|
||||
driven by a model attribute.
|
||||
|
||||
我们来构建一个简单的动画,它会让一个元素用模型驱动的方式在两个状态之间转场。
|
||||
你可以构建一个简单的动画,它会让一个元素用模型驱动的方式在两个状态之间转场。
|
||||
|
||||
Animations can be defined inside `@Component` metadata.
|
||||
|
||||
|
@ -112,14 +112,14 @@ hero is active, the element appears in a slightly larger size and lighter color.
|
|||
In this example, you are defining animation styles (color and transform) inline in the
|
||||
animation metadata.
|
||||
|
||||
在这个例子中,我们在元数据中用内联的方式定义了动画样式(`color` 和 `transform`)。在即将到来的一个 Angular 版本中,还将支持从组件的 CSS 样式表中提取样式。
|
||||
在这个例子中,你在元数据中用内联的方式定义了动画样式(`color` 和 `transform`)。在即将到来的一个 Angular 版本中,还将支持从组件的 CSS 样式表中提取样式。
|
||||
|
||||
</div>
|
||||
|
||||
Now, using the `[@triggerName]` syntax, attach the animation that you just defined to
|
||||
one or more elements in the component's template.
|
||||
|
||||
我们刚刚定义了一个动画,但它还没有被用到任何地方。要想使用它,可以在模板中用 `[@triggerName]` 语法来把它附加到一个或多个元素上。
|
||||
现在,使用 `[@triggerName]` 语法来把刚刚定义的动画附加到组件模板中一个或多个元素上。
|
||||
|
||||
<code-example path="animations/src/app/hero-list-basic.component.ts" region="template" title="hero-list-basic.component.ts (excerpt)" linenums="false"></code-example>
|
||||
|
||||
|
@ -127,8 +127,8 @@ Here, the animation trigger applies to every element repeated by an `ngFor`. Eac
|
|||
the repeated elements animates independently. The value of the
|
||||
attribute is bound to the expression `hero.state` and is always either `active` or `inactive`.
|
||||
|
||||
这里,我们把该动画触发器添加到了由 `ngFor` 重复出来的每一个元素上。每个重复出来的元素都有独立的动画效果。
|
||||
然后把 `@triggerName` 属性(Attribute)的值设置成表达式 `hero.state`。这个值应该或者是 `inactive` 或者是 `active`,因为我们刚刚为它们俩定义过动画状态。
|
||||
这里,动画触发器被添加到了由 `ngFor` 重复出来的每一个元素上。每个重复出来的元素都有独立的动画效果。
|
||||
然后把 `@triggerName` 属性(Attribute)的值设置成表达式 `hero.state`。这个值应该是 `inactive` 或 `active` 之一。
|
||||
|
||||
With this setup, an animated transition appears whenever a hero object changes state.
|
||||
Here's the full component implementation:
|
||||
|
@ -152,12 +152,12 @@ hero objects. The source of the state can be a simple object attribute, as it wa
|
|||
or it can be a value computed in a method. The important thing is that you can read it into the
|
||||
component's template.
|
||||
|
||||
动画状态是一个由程序代码中定义的字符串值。在上面的例子中,基于英雄对象的逻辑状态,我们使用了 `'active'` 和 `'inactive'` 这两种状态。
|
||||
状态的来源可以是像本例中这样简单的对象属性,也可以是由方法计算出来的值。重点是,我们得能从组件模板中读取它。
|
||||
动画状态是一个由程序代码中定义的字符串值。在上面的例子中,`'active'` 和 `'inactive'` 是基于英雄对象的逻辑状态的。
|
||||
状态的来源可以是像本例中这样简单的对象属性,也可以是由方法计算出来的值。重点是,你要能从组件模板中读取它。
|
||||
|
||||
You can define *styles* for each animation state:
|
||||
|
||||
我们可以为每个动画状态定义了*一组样式*:
|
||||
你可以为每个动画状态定义了*一组样式*:
|
||||
|
||||
<code-example path="animations/src/app/hero-list-basic.component.ts" region="states" title="src/app/hero-list-basic.component.ts" linenums="false"></code-example>
|
||||
|
||||
|
@ -310,7 +310,7 @@ using the hero state as the animation state. This lets you configure
|
|||
different transitions for entering and leaving based on what the state of the hero
|
||||
is:
|
||||
|
||||
通过把英雄的状态用作动画的状态,还能把该动画跟以前的转场动画组合成一个复合动画。这让我们能根据该英雄的当前状态为其配置不同的进场与离场动画:
|
||||
通过把英雄的状态用作动画的状态,还能把该动画跟以前的转场动画组合成一个复合动画。这让你能根据该英雄的当前状态为其配置不同的进场与离场动画:
|
||||
|
||||
* Inactive hero enter: `void => inactive`
|
||||
|
||||
|
@ -381,7 +381,7 @@ For example, elements often have widths and heights that
|
|||
depend on their content and the screen size. These properties are often tricky
|
||||
to animate with CSS.
|
||||
|
||||
有时候,我们想在动画中使用的尺寸类样式,它的值在开始运行之前都是不可知的。比如,元素的宽度和高度往往依赖于它们的内容和屏幕的尺寸。处理这些属性对 CSS 动画而言通常是相当棘手的。
|
||||
有时候,你在开始运行之前都无法知道某个样式属性的值。比如,元素的宽度和高度往往依赖于它们的内容和屏幕的尺寸。处理这些属性对 CSS 动画而言通常是相当棘手的。
|
||||
|
||||
In these cases, you can use a special `*` property value so that the value of the
|
||||
property is computed at runtime and then plugged into the animation.
|
||||
|
@ -494,7 +494,7 @@ which marks the beginning of the animation, and one, which marks the end.
|
|||
This example adds some "bounce" to the enter and leave animations with
|
||||
keyframes:
|
||||
|
||||
在这个例子中,我们使用关键帧来为进场和离场动画添加一些“反弹效果”:
|
||||
这个例子使用关键帧来为进场和离场动画添加一些“反弹效果”:
|
||||
|
||||
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" title="hero-list-multistep.component.ts (excerpt)" linenums="false"></code-example>
|
||||
|
||||
|
@ -519,19 +519,19 @@ offsets receive offsets `0`, `0.5`, and `1`.
|
|||
You've seen how to animate multiple style properties at the same time:
|
||||
just put all of them into the same `style()` definition.
|
||||
|
||||
我们已经知道该如何在同一时间段进行多个样式的动画了:只要把它们都放进同一个 `style()` 定义中就行了!
|
||||
你已经知道该如何在同一时间段进行多个样式的动画了:只要把它们都放进同一个 `style()` 定义中就行了!
|
||||
|
||||
But you may also want to configure different *timings* for animations that happen
|
||||
in parallel. For example, you may want to animate two CSS properties but use a
|
||||
different easing function for each one.
|
||||
|
||||
但我们也可能会希望为同时发生的几个动画配置不同的*时间线*。比如,同时对两个 CSS 属性做动画,但又得为它们定义不同的缓动函数。
|
||||
但你也可能会希望为同时发生的几个动画配置不同的*时间线*。比如,同时对两个 CSS 属性做动画,但又得为它们定义不同的缓动函数。
|
||||
|
||||
For this you can use animation *groups*. In this example, using groups both on
|
||||
enter and leave allows for two different timing configurations. Both
|
||||
are applied to the same element in parallel, but run independently of each other:
|
||||
|
||||
这种情况下就可以用动画*组*来解决了。在这个例子中,我们同时在进场和离场时使用了组,以便能让它们使用两种不同的时间线配置。
|
||||
这种情况下就可以用动画*组*来解决了。在这个例子中,同时在进场和离场时使用了组,以便能让它们使用两种不同的时间线配置。
|
||||
它们被同时应用到同一个元素上,但又彼此独立运行:
|
||||
|
||||
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" title="hero-list-groups.component.ts (excerpt)" linenums="false"></code-example>
|
||||
|
@ -551,7 +551,7 @@ A callback is fired when an animation is started and also when it is done.
|
|||
In the keyframes example, you have a `trigger` called `@flyInOut`. You can hook
|
||||
those callbacks like this:
|
||||
|
||||
对于例子中的这个关键帧,我们有一个叫做 `@flyInOut` 的 `trigger`。在那里我们可以挂钩到那些回调,比如:
|
||||
对于例子中的这个关键帧,你有一个叫做 `@flyInOut` 的 `trigger`。在那里你可以挂钩到那些回调,比如:
|
||||
|
||||
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" title="hero-list-multistep.component.ts (excerpt)" linenums="false"></code-example>
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# The Ahead-of-Time (AOT) Compiler
|
||||
|
||||
# 预先(AOT)编译
|
||||
|
||||
The Angular Ahead-of-Time (AOT) compiler converts your Angular HTML and TypeScript code into efficient JavaScript code during the build phase _before_ the browser downloads and runs that code.
|
||||
|
||||
Angular 的“预先(AOT)编译器”会在构建期间把 Angular 应用的 HTML 和 TypeScript 代码编译成高效的 JavaScript 代码,之后浏览器就可以下载并快速运行这些代码。
|
||||
|
@ -26,14 +28,25 @@ An Angular application consists largely of components and their HTML templates.
|
|||
Before the browser can render the application,
|
||||
the components and templates must be converted to executable JavaScript by an _Angular compiler_.
|
||||
|
||||
Angular 应用由大量组件及其 HTML 模板组成。
|
||||
在浏览器渲染应用之前,组件和模板必须由 *Angular 编译器*转换成可执行的 JavaScript 代码。
|
||||
|
||||
Angular offers two ways to compile your application:
|
||||
|
||||
Angular 提供了两种方式来编译你的应用:
|
||||
|
||||
1. **_Just-in-Time_ (JIT)**, which compiles your app in the browser at runtime
|
||||
|
||||
**即时(JIT)编译**,它会在浏览器中运行时编译你的应用
|
||||
|
||||
1. **_Ahead-of-Time_ (AOT)**, which compiles your app at build time.
|
||||
|
||||
**预先(AOT)编译**,它会在构建时编译你的应用。
|
||||
|
||||
JIT compilation is the default when you run the _build-only_ or the _build-and-serve-locally_ CLI commands:
|
||||
|
||||
当你运行 *`build`* 或 *`serve`* 这两个 CLI 命令时 JIT 编译是默认选项:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
|
||||
ng build
|
||||
|
@ -45,6 +58,8 @@ JIT compilation is the default when you run the _build-only_ or the _build-and-s
|
|||
|
||||
For AOT compilation, append the `--aot` flags to the _build-only_ or the _build-and-serve-locally_ CLI commands:
|
||||
|
||||
要进行 AOT 编译只要给这两个 CLI 命令添加 `--aot` 标志就行了:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
|
||||
ng build --aot
|
||||
|
@ -56,6 +71,8 @@ For AOT compilation, append the `--aot` flags to the _build-only_ or the _build-
|
|||
|
||||
The `--prod` meta-flag compiles with AOT by default.
|
||||
|
||||
`--prod` 标志也会默认使用 AOT 编译。
|
||||
|
||||
See the [CLI documentation](https://github.com/angular/angular-cli/wiki) for details, especially the [`build` topic](https://github.com/angular/angular-cli/wiki/build).
|
||||
|
||||
要了解更多,请参见[CLI 文档](https://github.com/angular/angular-cli/wiki),特别是[`build` 这个主题](https://github.com/angular/angular-cli/wiki/build)。
|
||||
|
@ -122,9 +139,14 @@ AOT 编译远在 HTML 模版和组件被服务到客户端之前,将它们编
|
|||
|
||||
## Angular Compiler Options
|
||||
|
||||
## Angular 编译器选项
|
||||
|
||||
You can control your app compilation by providing template compiler options in the `tsconfig.json` file along with the options supplied to the TypeScript compiler. The template compiler options are specified as members of
|
||||
`"angularCompilerOptions"` object as shown below:
|
||||
|
||||
你可以通过在 `tsconfig.json` 文件中随 TypeScript 编译选项一起提供模板编译选项来控制应用的编译方式。
|
||||
这些模板编译选项都是作为 `"angularCompilerOptions"` 对象的成员指定的,代码如下:
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
|
@ -146,25 +168,42 @@ You can control your app compilation by providing template compiler options in t
|
|||
This option tells the compiler not to produce `.metadata.json` files.
|
||||
The option is `false` by default.
|
||||
|
||||
`.metadata.json` files contain infomration needed by the template compiler from a `.ts`
|
||||
这个选项告诉编译器不要生成 `.metadata.json` 文件,它默认是 `false`。
|
||||
|
||||
`.metadata.json` files contain information needed by the template compiler from a `.ts`
|
||||
file that is not included in the `.d.ts` file produced by the TypeScript compiler. This information contains,
|
||||
for example, the content of annotations (such as a component's template) which TypeScript
|
||||
emits to the `.js` file but not to the `.d.ts` file.
|
||||
|
||||
`.metadata.json` 文件中包含模板编译器所需的信息,这些信息来自于 `.ts` 文件中,但是没有包含在由 TypeScript 编译器生成的 `.d.ts` 文件中。
|
||||
比如,这个信息包括 TypeScript 发出的注解内容(如组件的模板),TypeScript 把它生成到了 `.js` 文件中,但是没有生成到 `.d.ts` 文件中。
|
||||
|
||||
This option should be set to `true` if using TypeScript's `--outFile` option, as the metadata files
|
||||
are not valid for this style of TypeScript output. It is not recommeded to use `--outFile` with
|
||||
Angular. Use a bundler, such as [webpack](https://webpack.js.org/), instead.
|
||||
|
||||
如果使用了 TypeScript 的 `--outFile` 选项,那就要同时设置这个选项。因为在 TypeScript 的这种输出方式下,metadata 文件是无效的。
|
||||
Angular 中不建议使用 `--outFile`,请改用 [webpack](https://webpack.js.org/) 之类的打包器代替。
|
||||
|
||||
This option can also be set to `true` when using factory summaries as the factory summaries
|
||||
include a copy of the information that is in the `.metadata.json` file.
|
||||
|
||||
当使用工厂汇总器(factory summary)时,这个选项也要设置为 `true`,因为工厂汇总器在自己的 `.metadata.json` 中也包含了这些信息的一个副本。
|
||||
|
||||
### *strictMetadataEmit*
|
||||
|
||||
This option tells the template compiler to report an error to the `.metadata.json`
|
||||
file if `"skipMetadataEmit"` is `false` . This option is `false` by default. This should only be used when `"skipMetadataEmit"` is `false` and `"skipTemplateCodeGen"` is `true`.
|
||||
|
||||
这个选项告诉模板编译器如果 `"skipMetadataEmit"` 为 `false`,那就把错误信息汇报到 `.metadata.json` 中。
|
||||
只有当 `"skipMetadataEmit"` 为 `false` 且 `"skipTemplateCodeGen"` 为 `true` 时才应该使用这个选项。
|
||||
|
||||
It is intended to validate the `.metadata.json` files emitted for bundling with an `npm` package. The validation is overly strict and can emit errors for metadata that would never produce an error when used by the template compiler. You can choose to suppress the error emitted by this option for an exported symbol by including `@dynamic` in the comment documenting the symbol.
|
||||
|
||||
它的设计意图是要验证为打包 `npm` 而生成的 `.metadata.json` 文件。
|
||||
这种验证非常严格,因此在使用模板编译器时可能会对那些铁定不会出错的元数据文件报告一些错误。
|
||||
你可以用 `@dynamic` 在注释中指定一些符号,来禁止对它们报告错误。
|
||||
|
||||
It is valid for `.metadata.json` files to contain errors. The template compiler reports these errors
|
||||
if the metadata is used to determine the contents of an annotation. The metadata
|
||||
collector cannot predict the symbols that are designed to use in an annotation, so it will preemptively
|
||||
|
@ -310,8 +349,8 @@ Angular 的元数据会告诉 Angular 如何创建应用中类的实例以及如
|
|||
You specify the metadata with **decorators** such as `@Component()` and `@Input()`.
|
||||
You also specify metadata implicitly in the constructor declarations of these decorated classes.
|
||||
|
||||
我们通过**装饰器**来指定元数据,比如 `@Component()` 和 `@Input()`。
|
||||
我们还可以在这些带装饰器的类的构造函数中隐式指定元数据。
|
||||
你通过**装饰器**来指定元数据,比如 `@Component()` 和 `@Input()`。
|
||||
你还可以在这些带装饰器的类的构造函数中隐式指定元数据。
|
||||
|
||||
In the following example, the `@Component()` metadata object and the class constructor tell Angular how to create and display an instance of `TypicalComponent`.
|
||||
|
||||
|
@ -330,7 +369,7 @@ export class TypicalComponent {
|
|||
|
||||
```
|
||||
|
||||
The Angular compiler extracts the metadata _once_ and generates a _factory_ for `TypicalComponent`.
|
||||
The Anglar compiler extracts the metadata _once_ and generates a _factory_ for `TypicalComponent`.
|
||||
When it needs to create a `TypicalComponent` instance, Angular calls the factory, which produces a new visual element, bound to a new instance of the component class with its injected dependency.
|
||||
|
||||
Angular 编译器只提取**一次**元数据,并且为 `TypicalComponent` 生成一个**工厂**。
|
||||
|
@ -342,7 +381,7 @@ Angular 编译器只提取**一次**元数据,并且为 `TypicalComponent` 生
|
|||
|
||||
You write metadata in a _subset_ of TypeScript that must conform to the following general constraints:
|
||||
|
||||
我们只能使用 TypeScript 的一个**子集**书写元数据,它必须满足下列限制:
|
||||
你只能使用 TypeScript 的一个**子集**书写元数据,它必须满足下列限制:
|
||||
|
||||
1. Limit [expression syntax](#expression-syntax) to the supported subset of JavaScript.
|
||||
|
||||
|
@ -362,7 +401,7 @@ You write metadata in a _subset_ of TypeScript that must conform to the followin
|
|||
|
||||
The next sections elaborate on these points.
|
||||
|
||||
我们将在下一节详细解释这些问题。
|
||||
下一节将会详细解释这些问题。
|
||||
|
||||
## How AOT works
|
||||
|
||||
|
@ -370,7 +409,7 @@ The next sections elaborate on these points.
|
|||
|
||||
It helps to think of the AOT compiler as having two phases: a code analysis phase in which it simply records a representation of the source; and a code generation phase in which the compiler's `StaticReflector` handles the interpretation as well as places restrictions on what it interprets.
|
||||
|
||||
我们可以把 AOT 编译器看做两个阶段:在代码分析阶段,它只记录源代码,而在代码生成阶段,编译器的 `StaticReflector` 会解释这些结果,并为这些结果加上限制。
|
||||
可以把 AOT 编译器看做两个阶段:在代码分析阶段,它只记录源代码,而在代码生成阶段,编译器的 `StaticReflector` 会解释这些结果,并为这些结果加上限制。
|
||||
|
||||
## Phase 1: analysis
|
||||
|
||||
|
@ -386,7 +425,7 @@ At the same time, the AOT **_collector_** analyzes the metadata recorded in the
|
|||
|
||||
You can think of `.metadata.json` as a diagram of the overall structure of a decorator's metadata, represented as an [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
|
||||
|
||||
我们可以把 `.metadata.json` 文件看做一个包括全部装饰器的元数据的全景图,就像[抽象语法树 (AST) ](https://en.wikipedia.org/wiki/Abstract_syntax_tree)一样。
|
||||
你可以把 `.metadata.json` 文件看做一个包括全部装饰器的元数据的全景图,就像[抽象语法树 (AST) ](https://en.wikipedia.org/wiki/Abstract_syntax_tree)一样。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -407,25 +446,25 @@ Define metadata objects with the following limited syntax:
|
|||
这个**收集器**只能理解 JavaScript 的一个子集。
|
||||
请使用下列受限语法定义元数据对象:
|
||||
|
||||
Syntax | Example
|
||||
-----------------------------------|-----------------------------------
|
||||
Literal object | `{cherry: true, apple: true, mincemeat: false}`
|
||||
Literal array | `['cherries', 'flour', 'sugar']`
|
||||
Spread in literal array | `['apples', 'flour', ...the_rest]`
|
||||
Calls | `bake(ingredients)`
|
||||
New | `new Oven()`
|
||||
Property access | `pie.slice`
|
||||
Array index | `ingredients[0]`
|
||||
Identifier reference | `Component`
|
||||
A template string | <code>`pie is ${multiplier} times better than cake`</code>
|
||||
Literal string | `'pi'`
|
||||
Literal number | `3.14153265`
|
||||
Literal boolean | `true`
|
||||
Literal null | `null`
|
||||
Supported prefix operator | `!cake`
|
||||
Supported Binary operator | `a + b`
|
||||
Conditional operator | `a ? b : c`
|
||||
Parentheses | `(a + b)`
|
||||
<t>Syntax</t><t>语法</t> | <t>Example</t><t>范例</t>
|
||||
<t>-----------------------------------</t><t></t> |-----------------------------------
|
||||
<t>Literal object</t><t>对象字面量</t> | `{cherry: true, apple: true, mincemeat: false}`
|
||||
<t>Literal array</t><t>数组字面量</t> | `['cherries', 'flour', 'sugar']`
|
||||
<t>Spread in literal array</t><t>字面量数组展开</t> | `['apples', 'flour', ...the_rest]`
|
||||
<t>Calls</t><t>调用</t> | `bake(ingredients)`
|
||||
<t>New</t><t>创建对象</t> | `new Oven()`
|
||||
<t>Property access</t><t>属性访问</t> | `pie.slice`
|
||||
<t>Array index</t><t>数组索引</t> | `ingredients[0]`
|
||||
<t>Identifier reference</t><t>标识符引用</t> | `Component`
|
||||
<t>A template string</t><t>模板字符串</t> | <code>`pie is ${multiplier} times better than cake`</code>
|
||||
<t>Literal string</t><t>字符串字面量</t> | `'pi'`
|
||||
<t>Literal number</t><t>数字字面量</t> | `3.14153265`
|
||||
<t>Literal boolean</t><t>逻辑字面量</t> | `true`
|
||||
<t>Literal null</t><t>空字面量</t> | `null`
|
||||
<t>Supported prefix operator</t><t>受支持的前缀操作符</t> | `!cake`
|
||||
<t>Supported Binary operator</t><t>受支持的二元操作符</t> | `a + b`
|
||||
<t>Conditional operator</t><t>条件操作符</t> | `a ? b : c`
|
||||
<t>Parentheses</t><t>括号</t> | `(a + b)`
|
||||
|
||||
If an expression uses unsupported syntax, the _collector_ writes an error node to the `.metadata.json` file. The compiler later reports the error if it needs that
|
||||
piece of metadata to generate the application code.
|
||||
|
|
|
@ -15,7 +15,7 @@ You write Angular applications by composing HTML *templates* with Angularized ma
|
|||
writing *component* classes to manage those templates, adding application logic in *services*,
|
||||
and boxing components and services in *modules*.
|
||||
|
||||
我们是这样写 Angular 应用的:用 Angular 扩展语法编写 HTML *模板*,
|
||||
你是这样编写 Angular 应用的:用 Angular 扩展语法编写 HTML *模板*,
|
||||
用*组件*类管理这些模板,用*服务*添加应用逻辑,
|
||||
用*模块*打包发布组件与服务。
|
||||
|
||||
|
@ -23,13 +23,13 @@ Then you launch the app by *bootstrapping* the _root module_.
|
|||
Angular takes over, presenting your application content in a browser and
|
||||
responding to user interactions according to the instructions you've provided.
|
||||
|
||||
然后,我们通过*引导*_根模块_来启动该应用。
|
||||
然后,你通过*引导**根模块*来启动该应用。
|
||||
Angular 在浏览器中接管、展现应用的内容,并根据我们提供的操作指令响应用户的交互。
|
||||
|
||||
Of course, there is more to it than this.
|
||||
You'll learn the details in the pages that follow. For now, focus on the big picture.
|
||||
|
||||
当然,这只是冰山一角。后面我们将学习更多的细节。不过,目前我们还是先关注全景图吧。
|
||||
当然,这只是冰山一角。后面你还会学到更多的细节。不过,目前还是先关注全景图吧。
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/overview2.png" alt="overview">
|
||||
|
@ -135,7 +135,7 @@ Here's a simple root module:
|
|||
Launch an application by _bootstrapping_ its root module.
|
||||
During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one.
|
||||
|
||||
我们通过_引导_根模块来启动应用。
|
||||
通过*引导*根模块来启动应用。
|
||||
在开发期间,你通常在一个 `main.ts` 文件中引导 `AppModule`,就像这样:
|
||||
|
||||
<code-example path="architecture/src/main.ts" title="src/main.ts" linenums="false"></code-example>
|
||||
|
@ -176,7 +176,7 @@ JavaScript 中,每个_文件_是一个模块,文件中定义的所有对象
|
|||
|
||||
These are two different and _complementary_ module systems. Use them both to write your apps.
|
||||
|
||||
这两个模块化系统是互补的,我们在写程序时都会用到。
|
||||
这两个模块化系统是不同但*互补*的,我们在写程序时都会用到。
|
||||
|
||||
### Angular libraries
|
||||
|
||||
|
@ -243,7 +243,7 @@ Hang in there. The confusion yields to clarity with time and experience.
|
|||
|
||||
A _component_ controls a patch of screen called a *view*.
|
||||
|
||||
_组件_负责控制屏幕上的一小块区域,我们称之为*视图*。
|
||||
_组件_负责控制屏幕上的一小块区域叫做*视图*。
|
||||
|
||||
For example, the following views are controlled by components:
|
||||
|
||||
|
@ -264,7 +264,7 @@ For example, the following views are controlled by components:
|
|||
You define a component's application logic—what it does to support the view—inside a class.
|
||||
The class interacts with the view through an API of properties and methods.
|
||||
|
||||
我们在类中定义组件的应用逻辑,为视图提供支持。
|
||||
你在类中定义组件的应用逻辑,为视图提供支持。
|
||||
组件通过一些由属性和方法组成的 API 与视图交互。
|
||||
|
||||
{@a component-code}
|
||||
|
@ -295,7 +295,7 @@ Your app can take action at each moment in this lifecycle through optional [life
|
|||
You define a component's view with its companion **template**. A template is a form of HTML
|
||||
that tells Angular how to render the component.
|
||||
|
||||
我们通过组件的自带的**模板**来定义组件视图。模板以 HTML 形式存在,告诉 Angular 如何渲染组件。
|
||||
你通过组件的自带的**模板**来定义组件视图。模板以 HTML 形式存在,告诉 Angular 如何渲染组件。
|
||||
|
||||
A template looks like regular HTML, except for a few differences. Here is a
|
||||
template for our `HeroListComponent`:
|
||||
|
@ -351,7 +351,7 @@ There is no evidence of a framework, no "Angular" in it at all.
|
|||
|
||||
In fact, `HeroListComponent` really is *just a class*. It's not a component until you *tell Angular about it*.
|
||||
|
||||
实际上,`HeroListComponent` 真的*只是一个类*。直到我们*告诉 Angular* 它是一个组件。
|
||||
实际上,`HeroListComponent` 真的*只是一个类*。直到你*告诉 Angular* 它是一个组件。
|
||||
|
||||
To tell Angular that `HeroListComponent` is a component, attach **metadata** to the class.
|
||||
|
||||
|
@ -360,7 +360,7 @@ To tell Angular that `HeroListComponent` is a component, attach **metadata** to
|
|||
In TypeScript, you attach metadata by using a **decorator**.
|
||||
Here's some metadata for `HeroListComponent`:
|
||||
|
||||
在 TypeScript 中,我们用**装饰器 (decorator) **来附加元数据。
|
||||
在 TypeScript 中,你要用**装饰器 (decorator) **来附加元数据。
|
||||
下面就是 `HeroListComponent` 的一些元数据。
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
|
||||
|
@ -431,7 +431,7 @@ Without a framework, you would be responsible for pushing data values into the H
|
|||
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to
|
||||
read as any experienced jQuery programmer can attest.
|
||||
|
||||
如果没有框架,我们就得自己把数据值推送到 HTML 控件中,并把用户的反馈转换成动作和值更新。
|
||||
如果没有框架,你就得自己把数据值推送到 HTML 控件中,并把用户的反馈转换成动作和值更新。
|
||||
如果手工写代码来实现这些推/拉逻辑,肯定会枯燥乏味、容易出错,读起来简直是噩梦 —— 写过 jQuery 的程序员大概都对此深有体会。
|
||||
|
||||
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
|
||||
|
@ -441,7 +441,7 @@ a mechanism for coordinating parts of a template with parts of a component.
|
|||
Add binding markup to the template HTML to tell Angular how to connect both sides.
|
||||
|
||||
Angular 支持**数据绑定**,一种让模板的各部分与组件的各部分相互合作的机制。
|
||||
我们往模板 HTML 中添加绑定标记,来告诉 Angular 如何把二者联系起来。
|
||||
往模板 HTML 中添加绑定标记,来告诉 Angular 如何把二者联系起来。
|
||||
|
||||
As the diagram shows, there are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions.
|
||||
|
||||
|
@ -529,7 +529,7 @@ a `@Component` decorator is actually a `@Directive` decorator extended with temp
|
|||
While **a component is technically a directive**,
|
||||
components are so distinctive and central to Angular applications that this architectural overview separates components from directives.
|
||||
|
||||
虽然**严格来说组件就是一个指令**,但是组件非常独特,并在 Angular 中位于中心地位,所以在架构概览中,我们把组件从指令中独立了出来。
|
||||
虽然**严格来说组件就是一个指令**,但是组件非常独特,并在 Angular 中位于中心地位,所以在架构概览中把组件从指令中独立了出来。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -588,7 +588,7 @@ Angular 还有少量指令,它们或者修改结构布局(例如 [ngSwitch](
|
|||
Of course, you can also write your own directives. Components such as
|
||||
`HeroListComponent` are one kind of custom directive.
|
||||
|
||||
当然,我们也能编写自己的指令。像 `HeroListComponent` 这样的组件就是一种自定义指令。
|
||||
当然,你也能编写自己的指令。像 `HeroListComponent` 这样的组件就是一种自定义指令。
|
||||
|
||||
<!-- PENDING: link to where to learn more about other kinds! -->
|
||||
|
||||
|
@ -682,13 +682,13 @@ It delegates everything nontrivial to services.
|
|||
Angular doesn't *enforce* these principles.
|
||||
It won't complain if you write a "kitchen sink" component with 3000 lines.
|
||||
|
||||
Angular 不会*强制要求*我们遵循这些原则。
|
||||
即使我们花 3000 行代码写了一个“厨房洗碗槽”组件,它也不会抱怨什么。
|
||||
Angular 不会*强行保障*这些原则。
|
||||
即使你花 3000 行代码写了一个“厨房洗碗槽”组件,它也不会抱怨什么。
|
||||
|
||||
Angular does help you *follow* these principles by making it easy to factor your
|
||||
application logic into services and make those services available to components through *dependency injection*.
|
||||
|
||||
Angular 帮助我们*遵循*这些原则 —— 它让我们能轻易地把应用逻辑拆分到服务,并通过*依赖注入*来在组件中使用这些服务。
|
||||
Angular 帮助你*遵循*这些原则 —— 它让我们能轻易地把应用逻辑拆分到服务,并通过*依赖注入*来在组件中使用这些服务。
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -747,12 +747,12 @@ If the injector doesn't have a `HeroService`, how does it know how to make one?
|
|||
In brief, you must have previously registered a **provider** of the `HeroService` with the injector.
|
||||
A provider is something that can create or return a service, typically the service class itself.
|
||||
|
||||
简单点说,我们必须先用注入器(injector)为 `HeroService` 注册一个**提供商(provider)**。
|
||||
简单点说,你必须先用注入器(injector)为 `HeroService` 注册一个**提供商(provider)**。
|
||||
提供商用来创建或返回服务,通常就是这个服务类本身(相当于 `new HeroService()`)。
|
||||
|
||||
You can register providers in modules or in components.
|
||||
|
||||
我们可以在模块中或组件中注册提供商。
|
||||
你可以在模块中或组件中注册提供商。
|
||||
|
||||
In general, add providers to the [root module](guide/architecture#modules) so that
|
||||
the same instance of a service is available everywhere.
|
||||
|
@ -814,7 +814,7 @@ Points to remember about dependency injection:
|
|||
|
||||
You've learned the basics about the eight main building blocks of an Angular application:
|
||||
|
||||
我们学到的这些只是关于 Angular 应用程序的八个主要构造块的基础知识:
|
||||
你学到的这些只是关于 Angular 应用程序的八个主要构造块的基础知识:
|
||||
|
||||
* [Modules](guide/architecture#modules)
|
||||
|
||||
|
@ -853,7 +853,7 @@ and it's more than enough to get going.
|
|||
But it doesn't include everything you need to know.
|
||||
|
||||
这是 Angular 应用程序中所有其它东西的基础,要使用 Angular,以这些作为开端就绰绰有余了。
|
||||
但它仍然没有包含我们需要知道的全部。
|
||||
但它仍然没有包含你需要知道的一切。
|
||||
|
||||
Here is a brief, alphabetical list of other important Angular features and services.
|
||||
Most of them are covered in this documentation (or soon will be).
|
||||
|
|
|
@ -169,7 +169,7 @@ You use the `ElementRef`in the directive's constructor
|
|||
to [inject](guide/dependency-injection) a reference to the host DOM element,
|
||||
the element to which you applied `appHighlight`.
|
||||
|
||||
我们可以在指令的构造函数中注入 `ElementRef`,来引用宿主 DOM 元素,
|
||||
你可以在指令的构造函数中注入 `ElementRef`,来引用宿主 DOM 元素,
|
||||
|
||||
`ElementRef` grants direct access to the host DOM element
|
||||
through its `nativeElement` property.
|
||||
|
@ -188,7 +188,7 @@ This first implementation sets the background color of the host element to yello
|
|||
|
||||
To use the new `HighlightDirective`, add a paragraph (`<p>`) element to the template of the root `AppComponent` and apply the directive as an attribute.
|
||||
|
||||
运行应用,就会看到我们的指令确实高亮了段落中的文本。
|
||||
要想使用这个新的 `HighlightDirective`,就往根组件 `AppComponent` 的模板中添加一个 `<p>` 元素,并把该指令作为一个属性使用。
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" title="src/app/app.component.html" region="applied"></code-example>
|
||||
|
||||
|
@ -209,7 +209,7 @@ which sets the `<p>` element's background style to yellow.
|
|||
|
||||
总结:Angular 在**宿主**元素 `<p>` 上发现了一个 `appHighlight` 属性。
|
||||
然后它创建了一个 `HighlightDirective` 类的实例,并把所在元素的引用注入到了指令的构造函数中。
|
||||
在构造函数中,我们把 `<p>` 元素的背景设置为了黄色。
|
||||
在构造函数中,该指令把 `<p>` 元素的背景设置为了黄色。
|
||||
|
||||
{@a respond-to-user}
|
||||
|
||||
|
@ -227,7 +227,7 @@ and respond by setting or clearing the highlight color.
|
|||
|
||||
Begin by adding `HostListener` to the list of imported symbols.
|
||||
|
||||
先把 `HostListener` 加进导入列表中,同时再添加 `Input` 符号,因为我们很快就要用到它。
|
||||
先把 `HostListener` 加进导入列表中。
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
|
@ -302,11 +302,11 @@ Currently the highlight color is hard-coded _within_ the directive. That's infle
|
|||
In this section, you give the developer the power to set the highlight color while applying the directive.
|
||||
|
||||
高亮的颜色目前是硬编码在指令中的,这不够灵活。
|
||||
我们应该让指令的使用者可以指定要用哪种颜色进行高亮。
|
||||
在这一节中,你应该让指令的使用者可以指定要用哪种颜色进行高亮。
|
||||
|
||||
Begin by adding `Input` to the list of symbols imported from `@angular/core`.
|
||||
|
||||
我们先从 `@angular/core` 中导入 `Input`。
|
||||
先从 `@angular/core` 中导入 `Input`。
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
|
@ -352,7 +352,7 @@ Let it control the highlight color with a property binding.
|
|||
|
||||
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
|
||||
|
||||
很不错,但还可以更好。我们可以在应用该指令时在同一个属性中设置颜色,就像这样:
|
||||
很不错,但如果可以在应用该指令时在*同一个属性*中设置颜色就更好了,就像这样:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"></code-example>
|
||||
|
||||
|
@ -362,12 +362,12 @@ You're re-using the directive's attribute selector (`[appHighlight]`) to do both
|
|||
That's a crisp, compact syntax.
|
||||
|
||||
`[appHighlight]` 属性同时做了两件事:把这个高亮指令应用到了 `<p>` 元素上,并且通过属性绑定设置了该指令的高亮颜色。
|
||||
我们复用了该指令的属性选择器 `[appHighlight]` 来同时完成它们。
|
||||
你复用了该指令的属性选择器 `[appHighlight]` 来同时完成它们。
|
||||
这是清爽、简约的语法。
|
||||
|
||||
You'll have to rename the directive's `highlightColor` property to `appHighlight` because that's now the color property binding name.
|
||||
|
||||
我们还要把该指令的 `highlightColor` 改名为 `myHighlight`,因为它是颜色属性目前的绑定名。
|
||||
你还要把该指令的 `highlightColor` 改名为 `myHighlight`,因为它是颜色属性目前的绑定名。
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
|
||||
|
||||
|
@ -383,7 +383,7 @@ This is disagreeable. The word, `appHighlight`, is a terrible property name and
|
|||
|
||||
Fortunately you can name the directive property whatever you want _and_ **_alias it_** for binding purposes.
|
||||
|
||||
幸运的是,我们可以随意命名该指令的属性,并且**给它指定一个用于绑定的别名**。
|
||||
幸运的是,你可以随意命名该指令的属性,并且**给它指定一个用于绑定的别名**。
|
||||
|
||||
Restore the original property name and specify the selector as the alias in the argument to `@Input`.
|
||||
|
||||
|
@ -394,7 +394,7 @@ Restore the original property name and specify the selector as the alias in the
|
|||
_Inside_ the directive the property is known as `highlightColor`.
|
||||
_Outside_ the directive, where you bind to it, it's known as `appHighlight`.
|
||||
|
||||
在指令内部,该属性叫 `highlightColor`,在外部,当我们绑定到它时,它叫 `appHighlight`。
|
||||
在指令内部,该属性叫 `highlightColor`,在外部,你绑定到它地方,它叫 `appHighlight`。
|
||||
|
||||
You get the best of both worlds: the property name you want and the binding syntax you want:
|
||||
|
||||
|
@ -405,7 +405,7 @@ You get the best of both worlds: the property name you want and the binding synt
|
|||
Now that you're binding via the alias to the `highlightColor`, modify the `onMouseEnter()` method to use that property.
|
||||
If someone neglects to bind to `appHighlightColor`, highlight the host element in red:
|
||||
|
||||
现在,我们绑定到了 `highlightColor` 属性,并修改 `onMouseEnter()` 方法来使用它。
|
||||
现在,你通过别名绑定到了 `highlightColor` 属性,并修改 `onMouseEnter()` 方法来使用它。
|
||||
如果有人忘了绑定到 `appHighlightColor`,那就用红色进行高亮。
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
|
||||
|
@ -425,7 +425,7 @@ In this section, you'll turn `AppComponent` into a harness that
|
|||
lets you pick the highlight color with a radio button and bind your color choice to the directive.
|
||||
|
||||
凭空想象该指令如何工作可不容易。
|
||||
在本节,我们将把 `AppComponent` 改成一个测试程序,它让你可以通过单选按钮来选取高亮颜色,并且把你选取的颜色绑定到指令中。
|
||||
在本节,你将把 `AppComponent` 改成一个测试程序,它让你可以通过单选按钮来选取高亮颜色,并且把你选取的颜色绑定到指令中。
|
||||
|
||||
Update <code>app.component.html</code> as follows:
|
||||
|
||||
|
@ -461,7 +461,7 @@ At the moment, the default color—the color that prevails until
|
|||
the user picks a highlight color—is hard-coded as "red".
|
||||
Let the template developer set the default color.
|
||||
|
||||
目前,默认颜色(它在用户选取了高亮颜色之前一直有效)被硬编码为红色。我们要让模板的开发者也可以设置默认颜色。
|
||||
目前,默认颜色(它在用户选取了高亮颜色之前一直有效)被硬编码为红色。应该允许模板的开发者设置默认颜色。
|
||||
|
||||
Add a second **input** property to `HighlightDirective` called `defaultColor`:
|
||||
|
||||
|
@ -492,7 +492,7 @@ and fall back to "violet" as the default color.
|
|||
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
|
||||
because you made it _public_ with the `@Input` decorator.
|
||||
|
||||
Angular 之所以知道 `defaultColor` 绑定属于 `HighlightDirective`,是因为我们已经通过 `@Input` 装饰器把它设置成了*公共*属性。
|
||||
Angular 之所以知道 `defaultColor` 绑定属于 `HighlightDirective`,是因为你已经通过 `@Input` 装饰器把它设置成了*公共*属性。
|
||||
|
||||
Here's how the harness should work when you're done coding.
|
||||
|
||||
|
@ -554,7 +554,7 @@ You can also experience and download the <live-example title="Attribute Directiv
|
|||
In this demo, the `highlightColor` property is an ***input*** property of
|
||||
the `HighlightDirective`. You've seen it applied without an alias:
|
||||
|
||||
在这个例子中 `hightlightColor` 是 `HighlightDirective` 的一个***输入型***属性。我们见过它没有用别名时的代码:
|
||||
在这个例子中 `hightlightColor` 是 `HighlightDirective` 的一个***输入型***属性。你见过它没有用别名时的代码:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
|
||||
|
@ -574,7 +574,7 @@ Without `@Input`, Angular refuses to bind to the property.
|
|||
You've bound template HTML to component properties before and never used `@Input`.
|
||||
What's different?
|
||||
|
||||
但我们以前也曾经把模板 HTML 绑定到组件的属性,而且从来没有用过 `@Input`。
|
||||
但你以前也曾经把模板 HTML 绑定到组件的属性,而且从来没有用过 `@Input`。
|
||||
差异何在?
|
||||
|
||||
The difference is a matter of trust.
|
||||
|
|
|
@ -194,7 +194,7 @@ Angular 构建于 Web 平台的最新标准之上。
|
|||
You compensate by loading polyfill scripts ("polyfills") for the browsers that you must support.
|
||||
The [table below](#polyfill-libs) identifies most of the polyfills you might need.
|
||||
|
||||
我们可以通过加载腻子脚本("polyfills")来为想要支持的浏览器弥补这些特性。
|
||||
你可以通过加载腻子脚本("polyfills")来为想要支持的浏览器弥补这些特性。
|
||||
[下表](#polyfill-libs) 列出了可能用到的大多数腻子脚本。
|
||||
|
||||
<div class="alert is-important">
|
||||
|
@ -224,7 +224,7 @@ This file incorporates the mandatory and many of the optional polyfills as JavaS
|
|||
|
||||
The npm packages for the _mandatory_ polyfills (such as `zone.js`) were installed automatically for you when you created your project and their corresponding `import` statements are ready to go. You probably won't touch these.
|
||||
|
||||
**强制性** 腻子脚本(如 `zone.js`)的 npm 包在创建项目时就已经自动安装了,相应的 `import` 语句也都加好了。我们一般不用动它们。
|
||||
**强制性** 腻子脚本(如 `zone.js`)的 npm 包在创建项目时就已经自动安装了,相应的 `import` 语句也都加好了。你一般不用动它们。
|
||||
|
||||
But if you need an optional polyfill, you'll have to install its npm package.
|
||||
For example, [if you need the web animations polyfill](http://caniuse.com/#feat=web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent):
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
The Angular documentation is a living document with continuous improvements.
|
||||
This log calls attention to recent significant changes.
|
||||
|
||||
我们将持续不断的更新和改进 Angular 文档。本日志记录了近期最重要的变更。
|
||||
Angular 的文档将持续不断的更新和改进。本日志记录了近期最重要的变更。
|
||||
|
||||
## Updated to Angular 4.0. Documentation for Angular 2.x can be found at [v2.angular.io](https://v2.angular.io).
|
||||
|
||||
|
@ -61,7 +61,7 @@ It helps clearly separate app code from setup and configuration files.
|
|||
|
||||
所有的文档范例都已经向 Angular CLI 的默认文件夹结构看齐了。
|
||||
这是把范例迁移到 Angular CLI 过程中的一步。
|
||||
不过也不仅是为了迁移,它确实能帮我们把应用代码从环境代码和配置代码中分离出来。
|
||||
不过也不仅是为了迁移,它还能把应用代码从环境准备代码和配置代码中清晰地分离出来。
|
||||
|
||||
All samples now have a `src/` folder at the project root.
|
||||
The former `app/` folder moves under `src/`.
|
||||
|
@ -69,7 +69,7 @@ Read about moving your existing project to this structure in
|
|||
<a href="https://github.com/angular/quickstart#updating-to-a-newer-version-of-the-quickstart-repo" target="Migrating samples/quickstart app to the src folder">
|
||||
the QuickStart repo update instructions</a>.
|
||||
|
||||
我们已经把所有范例改成了使用项目根目录下的 `src/` 文件夹。
|
||||
所有的范例都改成了使用项目根目录下的 `src/` 文件夹。
|
||||
也就是把以前的 `app/` 文件夹移到了 `src/` 文件夹下面。
|
||||
要了解如何对你的现有项目进行这种迁移,请参阅<a href="https://github.com/angular/quickstart#updating-to-a-newer-version-of-the-quickstart-repo" target="_blank" target="把范例中的应用迁移到 src 文件夹">QuickStart 中的迁移指南</a>。
|
||||
|
||||
|
@ -403,10 +403,10 @@ Barrels now are far less useful and have been removed from the style guide;
|
|||
they remain valuable but are not a matter of Angular style.
|
||||
Also relaxed the rule that discouraged use of the `@Component.host` property.
|
||||
|
||||
[StyleGuide](guide/styleguide)解释了我们为 Angular 模块(NgModule)而推荐的约定。
|
||||
[StyleGuide](guide/styleguide)解释了 Angular 模块(NgModule)推荐的约定。
|
||||
现在,封装桶不再那么重要,风格指南已经移除了它们。
|
||||
它们仍然很有价值,但是它们与 Angular 风格无关。
|
||||
我们同时对**不推荐使用 `@Component.host` 属性**的规则有所放宽。
|
||||
同时,**不推荐使用 `@Component.host` 属性**的规则也有所放宽。
|
||||
|
||||
## _moduleId: module.id_ everywhere (2016-09-25)
|
||||
|
||||
|
@ -417,7 +417,7 @@ have been converted to _module-relative_ URLs.
|
|||
Added the `moduleId: module.id` property-and-value to their `@Component` metadata.
|
||||
|
||||
在所有使用 `templateUrl` 或者 `styleUrls` 来获取模板或样式的例子组件都被转换为**相对模块**的 URL。
|
||||
我们添加了 `moduleId: module.id` 到它们的 `@Component` 元数据。
|
||||
把 `moduleId: module.id` 添加到了它们的 `@Component` 元数据。
|
||||
|
||||
This change is a requirement for compilation with AOT compiler when the app loads
|
||||
modules with SystemJS as the samples currently do.
|
||||
|
|
|
@ -291,7 +291,7 @@ countdown status message in its own template.
|
|||
|
||||
The `CountdownLocalVarParentComponent` that hosts the timer component is as follows:
|
||||
|
||||
让我们来看看计时器组件的宿主组件 `CountdownLocalVarParentComponent`。
|
||||
计时器组件的宿主组件 `CountdownLocalVarParentComponent` 如下:
|
||||
|
||||
<code-example path="component-interaction/src/app/countdown-parent.component.ts" region="lv" title="component-interaction/src/app/countdown-parent.component.ts">
|
||||
|
||||
|
@ -311,7 +311,7 @@ That gives you a reference to the child component and the ability to access
|
|||
This example wires parent buttons to the child's `start` and `stop` and
|
||||
uses interpolation to display the child's `seconds` property.
|
||||
|
||||
在这个例子中,我们把父组件的按钮绑定到子组件的 `start` 和 `stop` 方法,并用插值表达式来显示子组件的 `seconds` 属性。
|
||||
这个例子把父组件的按钮绑定到子组件的 `start` 和 `stop` 方法,并用插值表达式来显示子组件的 `seconds` 属性。
|
||||
|
||||
Here we see the parent and child working together.
|
||||
|
||||
|
@ -369,7 +369,7 @@ Neither its appearance nor its behavior will change.
|
|||
The child [CountdownTimerComponent](guide/component-interaction#countdown-timer-example) is the same as well.
|
||||
|
||||
下面的例子用与[倒计时](guide/component-interaction#countdown-timer-example)相同的范例来解释这种技术。
|
||||
我们没有改变它的外观或行为。子组件[CountdownTimerComponent](guide/component-interaction#countdown-timer-example)也和原来一样。
|
||||
它的外观或行为没有变化。子组件[CountdownTimerComponent](guide/component-interaction#countdown-timer-example)也和原来一样。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -415,14 +415,14 @@ The `ngAfterViewInit()` lifecycle hook is an important wrinkle.
|
|||
The timer component isn't available until *after* Angular displays the parent view.
|
||||
So it displays `0` seconds initially.
|
||||
|
||||
`ngAfterViewInit()` 生命周期钩子是非常重要的一步。被注入的计时器组件只有在 Angular 显示了父组件视图之后才能访问,所以我们先把秒数显示为 0.
|
||||
`ngAfterViewInit()` 生命周期钩子是非常重要的一步。被注入的计时器组件只有在 Angular 显示了父组件视图之后才能访问,所以它先把秒数显示为 0.
|
||||
|
||||
Then Angular calls the `ngAfterViewInit` lifecycle hook at which time it is *too late*
|
||||
to update the parent view's display of the countdown seconds.
|
||||
Angular's unidirectional data flow rule prevents updating the parent view's
|
||||
in the same cycle. The app has to *wait one turn* before it can display the seconds.
|
||||
|
||||
然后 Angular 会调用 `ngAfterViewInit` 生命周期钩子,但这时候再更新父组件视图的倒计时就已经太晚了。Angular 的单向数据流规则会阻止在同一个周期内更新父组件视图。我们在显示秒数之前会被迫*再等一轮*。
|
||||
然后 Angular 会调用 `ngAfterViewInit` 生命周期钩子,但这时候再更新父组件视图的倒计时就已经太晚了。Angular 的单向数据流规则会阻止在同一个周期内更新父组件视图。应用在显示秒数之前会被迫*再等一轮*。
|
||||
|
||||
Use `setTimeout()` to wait one tick and then revise the `seconds()` method so
|
||||
that it takes future values from the timer component.
|
||||
|
|
|
@ -6,21 +6,21 @@ Angular applications are styled with standard CSS. That means you can apply
|
|||
everything you know about CSS stylesheets, selectors, rules, and media queries
|
||||
directly to Angular applications.
|
||||
|
||||
Angular 应用使用标准的 CSS 来设置样式。这意味着我们可以把关于 CSS
|
||||
的那些知识和技能直接用于我们的 Angular 程序中,例如:样式表、选择器、规则以及媒体查询等。
|
||||
Angular 应用使用标准的 CSS 来设置样式。这意味着你可以把关于 CSS
|
||||
的那些知识和技能直接用于 Angular 程序中,例如:样式表、选择器、规则以及媒体查询等。
|
||||
|
||||
Additionally, Angular can bundle *component styles*
|
||||
with components, enabling a more modular design than regular stylesheets.
|
||||
|
||||
另外,Angular 还能把*组件样式*捆绑在我们的组件上,以实现比标准样式表更加模块化的设计。
|
||||
另外,Angular 还能把*组件样式*捆绑在组件上,以实现比标准样式表更加模块化的设计。
|
||||
|
||||
This page describes how to load and apply these component styles.
|
||||
|
||||
在本章中,我们将学到如何加载和使用这些*组件样式*。
|
||||
本章将会讲解如何加载和使用这些*组件样式*。
|
||||
|
||||
You can run the <live-example></live-example> in Stackblitz and download the code from there.
|
||||
|
||||
运行<live-example></live-example>来试用本页的代码。
|
||||
你可以运行<live-example></live-example>来在 Stackblitz 中试用并下载本页的代码。
|
||||
|
||||
## Using component styles
|
||||
|
||||
|
@ -30,7 +30,7 @@ For every Angular component you write, you may define not only an HTML template,
|
|||
but also the CSS styles that go with that template,
|
||||
specifying any selectors, rules, and media queries that you need.
|
||||
|
||||
对于我们写的每个 Angular 组件来说,除了定义 HTML 模板之外,我们还要定义用于模板的 CSS 样式、
|
||||
对你编写的每个 Angular 组件来说,除了定义 HTML 模板之外,我们还要定义用于模板的 CSS 样式、
|
||||
指定任意的选择器、规则和媒体查询。
|
||||
|
||||
One way to do this is to set the `styles` property in the component metadata.
|
||||
|
@ -39,7 +39,7 @@ Usually you give it one string, as in the following example:
|
|||
|
||||
实现方式之一,是在组件的元数据中设置 `styles` 属性。
|
||||
`styles` 属性可以接受一个包含 CSS 代码的字符串数组。
|
||||
通常我们只给它一个字符串就行了,如同下例:
|
||||
通常你只给它一个字符串就行了,如同下例:
|
||||
|
||||
<code-example path="component-styles/src/app/hero-app.component.ts" title="src/app/hero-app.component.ts" linenums="false">
|
||||
|
||||
|
@ -81,17 +81,17 @@ This scoping restriction is a ***styling modularity feature***.
|
|||
|
||||
* Changes to styles elsewhere in the application don't affect the component's styles.
|
||||
|
||||
我们组件的样式*不会*因为别的地方修改了样式而被意外改变。
|
||||
组件的样式*不会*因为别的地方修改了样式而被意外改变。
|
||||
|
||||
* You can co-locate the CSS code of each component with the TypeScript and HTML code of the component,
|
||||
which leads to a neat and tidy project structure.
|
||||
|
||||
我们可以让每个组件的 CSS 代码和它的 TypeScript、HTML 代码放在一起,这将促成清爽整洁的项目结构。
|
||||
你可以让每个组件的 CSS 代码和它的 TypeScript、HTML 代码放在一起,这将促成清爽整洁的项目结构。
|
||||
|
||||
* You can change or remove component CSS code without searching through the
|
||||
whole application to find where else the code is used.
|
||||
|
||||
将来我们可以修改或移除组件的 CSS 代码,而不用遍历整个应用来看它有没有被别处用到,只要看看当前组件就可以了。
|
||||
将来你可以修改或移除组件的 CSS 代码,而不用遍历整个应用来看它有没有被别处用到,只要看看当前组件就可以了。
|
||||
|
||||
{@a special-selectors}
|
||||
|
||||
|
@ -123,7 +123,7 @@ The `:host` selector is the only way to target the host element. You can't reach
|
|||
the host element from inside the component with other selectors because it's not part of the
|
||||
component's own template. The host element is in a parent component's template.
|
||||
|
||||
这是我们能以宿主元素为目标的*唯一*方式。除此之外,我们将没办法指定它,
|
||||
`:host` 选择是是把宿主元素作为目标的*唯一*方式。除此之外,你将没办法指定它,
|
||||
因为宿主不是组件自身模板的一部分,而是父组件模板的一部分。
|
||||
|
||||
Use the *function form* to apply host styles conditionally by
|
||||
|
@ -133,7 +133,7 @@ including another selector inside parentheses after `:host`.
|
|||
|
||||
The next example targets the host element again, but only when it also has the `active` CSS class.
|
||||
|
||||
在下一个例子中,我们又一次把宿主元素作为目标,但是只有当它同时带有 `active` CSS 类的时候才会生效。
|
||||
下一个例子再次把宿主元素作为目标,但是只有当它同时带有 `active` CSS 类的时候才会生效。
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" title="src/app/hero-details.component.css" linenums="false">
|
||||
|
||||
|
@ -148,7 +148,7 @@ For example, a CSS theme class could be applied to the document `<body>` element
|
|||
you want to change how your component looks based on that.
|
||||
|
||||
有时候,基于某些来自组件视图*外部*的条件应用样式是很有用的。
|
||||
例如,在文档的 `<body>` 元素上可能有一个用于表示样式主题 (theme) 的 CSS 类,而我们应当基于它来决定组件的样式。
|
||||
例如,在文档的 `<body>` 元素上可能有一个用于表示样式主题 (theme) 的 CSS 类,你应当基于它来决定组件的样式。
|
||||
|
||||
Use the `:host-context()` pseudo-class selector, which works just like the function
|
||||
form of `:host()`. The `:host-context()` selector looks for a CSS class in any ancestor of the component host element,
|
||||
|
@ -160,7 +160,7 @@ up to the document root. The `:host-context()` selector is useful when combined
|
|||
The following example applies a `background-color` style to all `<h2>` elements *inside* the component, only
|
||||
if some ancestor element has the CSS class `theme-light`.
|
||||
|
||||
在下面的例子中,只有当某个祖先元素有 CSS 类 `theme-light` 时,我们才会把 `background-color` 样式应用到组件*内部*的所有 `<h2>` 元素中。
|
||||
在下面的例子中,只有当某个祖先元素有 CSS 类 `theme-light` 时,才会把 `background-color` 样式应用到组件*内部*的所有 `<h2>` 元素中。
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" title="src/app/hero-details.component.css" linenums="false">
|
||||
|
||||
|
@ -179,12 +179,12 @@ component tree into all the child component views.
|
|||
The `/deep/` combinator works to any depth of nested components, and it applies to both the view
|
||||
children and content children of the component.
|
||||
|
||||
我们可以使用 `/deep/` 选择器,来强制一个样式对各级子组件的视图也生效,它*不但作用于组件的子视图,也会作用于组件的内容*。
|
||||
可以使用 `/deep/` 选择器来强制一个样式对各级子组件的视图也生效,它*不但作用于组件的子视图,也会作用于组件的内容*。
|
||||
|
||||
The following example targets all `<h3>` elements, from the host element down
|
||||
through this component to all of its child elements in the DOM.
|
||||
|
||||
在这个例子中,我们以所有的 `<h3>` 元素为目标,从宿主元素到当前元素再到 DOM 中的所有子元素:
|
||||
这个例子以所有的 `<h3>` 元素为目标,从宿主元素到当前元素再到 DOM 中的所有子元素:
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" title="src/app/hero-details.component.css" linenums="false">
|
||||
|
||||
|
@ -250,7 +250,7 @@ The scoping rules outlined earlier apply to each of these loading patterns.
|
|||
|
||||
You can add a `styles` array property to the `@Component` decorator.
|
||||
|
||||
我们可以给 `@Component` 装饰器添加一个 `styles` 数组型属性。
|
||||
你可以给 `@Component` 装饰器添加一个 `styles` 数组型属性。
|
||||
|
||||
Each string in the array defines some CSS for this component.
|
||||
|
||||
|
@ -287,7 +287,7 @@ ng generate component hero-app --inline-style
|
|||
You can load styles from external CSS files by adding a `styleUrls` property
|
||||
to a component's `@Component` decorator:
|
||||
|
||||
我们可以通过把外部 CSS 文件添加到 `@Component` 的 `styleUrls` 属性中来加载外部样式。
|
||||
你可以通过把外部 CSS 文件添加到 `@Component` 的 `styleUrls` 属性中来加载外部样式。
|
||||
|
||||
<code-tabs>
|
||||
|
||||
|
@ -310,7 +310,7 @@ They are _not inherited_ by any components nested within the template nor by any
|
|||
|
||||
You can specify more than one styles file or even a combination of `style` and `styleUrls`.
|
||||
|
||||
我们可以指定多个样式文件,甚至可以组合使用 `style` 和 `styleUrls` 方式。
|
||||
你可以指定多个样式文件,甚至可以组合使用 `style` 和 `styleUrls` 方式。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -331,7 +331,7 @@ ng generate component hero-app
|
|||
You can embed CSS styles directly into the HTML template by putting them
|
||||
inside `<style>` tags.
|
||||
|
||||
我们也可以在组件的 HTML 模板中嵌入 `<style>` 标签。
|
||||
你也可以在组件的 HTML 模板中嵌入 `<style>` 标签。
|
||||
|
||||
<code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles" title="src/app/hero-controls.component.ts">
|
||||
|
||||
|
@ -343,7 +343,7 @@ inside `<style>` tags.
|
|||
|
||||
You can also write `<link>` tags into the component's HTML template.
|
||||
|
||||
我们也可以在组件的 HTML 模板中写 `<link>` 标签。
|
||||
你也可以在组件的 HTML 模板中写 `<link>` 标签。
|
||||
|
||||
<code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink" title="src/app/hero-team.component.ts">
|
||||
|
||||
|
@ -370,12 +370,12 @@ You can also import CSS files into the CSS files using the standard CSS `@import
|
|||
For details, see [`@import`](https://developer.mozilla.org/en/docs/Web/CSS/@import)
|
||||
on the [MDN](https://developer.mozilla.org) site.
|
||||
|
||||
我们还可以利用标准的 CSS [`@import` 规则](https://developer.mozilla.org/en/docs/Web/CSS/@import)来把其它
|
||||
CSS 文件导入到我们的 CSS 文件中。
|
||||
你还可以利用标准的 CSS [`@import` 规则](https://developer.mozilla.org/en/docs/Web/CSS/@import)来把其它
|
||||
CSS 文件导入到 CSS 文件中。
|
||||
|
||||
In this case, the URL is relative to the CSS file into which you're importing.
|
||||
|
||||
在*这种*情况下,URL 是相对于我们执行导入操作的 CSS 文件的。
|
||||
在*这种*情况下,URL 是相对于你正在导入的 CSS 文件的。
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="import" title="src/app/hero-details.component.css (excerpt)">
|
||||
|
||||
|
@ -453,7 +453,7 @@ To control how this encapsulation happens on a *per
|
|||
component* basis, you can set the *view encapsulation mode* in the component metadata.
|
||||
Choose from the following modes:
|
||||
|
||||
通过在组件的元数据上设置*视图封装模式*,我们可以分别控制*每个组件*的封装模式。
|
||||
通过在组件的元数据上设置*视图封装模式*,你可以分别控制*每个组件*的封装模式。
|
||||
可选的封装模式一共有如下几种:
|
||||
|
||||
* `Native` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
|
@ -496,7 +496,7 @@ which is why `Emulated` view encapsulation is the default mode and recommended
|
|||
in most cases.
|
||||
|
||||
原生(`Native`)模式只适用于[有原生 Shadow DOM 支持的浏览器](http://caniuse.com/#feat=shadowdom)。
|
||||
因此仍然受到很多限制,这就是为什么我们会把仿真 (`Emulated`) 模式作为默认选项,并建议将其用于大多数情况。
|
||||
因此仍然受到很多限制,这就是为什么仿真 (`Emulated`) 模式是默认选项,并建议将其用于大多数情况。
|
||||
|
||||
{@a inspect-generated-css}
|
||||
|
||||
|
@ -513,7 +513,7 @@ In the DOM of a running Angular application with emulated view
|
|||
encapsulation enabled, each DOM element has some extra attributes
|
||||
attached to it:
|
||||
|
||||
当我们查看启用了仿真模式的 Angular 应用时,我们看到每个 DOM 元素都被加上了一些额外的属性。
|
||||
在启用了仿真模式的 Angular 应用的 DOM 树中,每个 DOM 元素都被加上了一些额外的属性。
|
||||
|
||||
<code-example format="">
|
||||
|
||||
|
@ -528,7 +528,7 @@ attached to it:
|
|||
|
||||
There are two kinds of generated attributes:
|
||||
|
||||
我们看到了两种被生成的属性:
|
||||
生成出的属性分为两种:
|
||||
|
||||
* An element that would be a shadow DOM host in native encapsulation has a
|
||||
generated `_nghost` attribute. This is typically the case for component host elements.
|
||||
|
@ -545,8 +545,8 @@ The exact values of these attributes aren't important. They are automatically
|
|||
generated and you never refer to them in application code. But they are targeted
|
||||
by the generated component styles, which are in the `<head>` section of the DOM:
|
||||
|
||||
这些属性的具体值并不重要。它们是自动生成的,并且我们永远不会在程序代码中直接引用到它们。
|
||||
但它们会作为生成的组件样式的目标,就像我们在 DOM 的 `<head>` 区所看到的:
|
||||
这些属性的具体值并不重要。它们是自动生成的,并且你永远不会在程序代码中直接引用到它们。
|
||||
但它们会作为生成的组件样式的目标,就像 DOM 的 `<head>` 中一样:
|
||||
|
||||
<code-example format="">
|
||||
|
||||
|
@ -567,5 +567,5 @@ with `_nghost` or `_ngcontent` attribute selectors.
|
|||
These extra selectors enable the scoping rules described in this page.
|
||||
|
||||
|
||||
这些就是我们写的那些样式被处理后的结果,于是每个选择器都被增加了 `_nghost` 或 `_ngcontent` 属性选择器。
|
||||
在这些附加选择器的帮助下,我们实现了本指南中所描述的这些作用域规则。
|
||||
这些就是那些样式被处理后的结果,每个选择器都被增加了 `_nghost` 或 `_ngcontent` 属性选择器。
|
||||
这些额外的选择器实现了本文所描述的这些作用域规则。
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
Dependency Injection is a powerful pattern for managing code dependencies.
|
||||
This cookbook explores many of the features of Dependency Injection (DI) in Angular.
|
||||
|
||||
依赖注入是一个用来管理代码依赖的强大模式。在这本“烹饪宝典”中,我们会讨论 Angular 依赖注入的许多特性。
|
||||
依赖注入是一个用来管理代码依赖的强大模式。本文会讨论 Angular 依赖注入的许多特性。
|
||||
|
||||
{@a toc}
|
||||
|
||||
|
@ -56,7 +56,7 @@ Angular 拿到“类提供商”之后,会通过 `new` 操作来新建服务
|
|||
Now that you've registered these services,
|
||||
Angular can inject them into the constructor of *any* component or service, *anywhere* in the application.
|
||||
|
||||
现在我们已经注册了这些服务,这样 Angular 就能在应用程序的*任何地方*,把它们注入到*任何*组件和服务的构造函数里。
|
||||
现在你已经注册了这些服务,这样 Angular 就能在应用程序的*任何地方*,把它们注入到*任何*组件和服务的构造函数里。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" title="src/app/hero-bios.component.ts (component constructor injection)" linenums="false">
|
||||
|
||||
|
@ -74,7 +74,7 @@ Angular can inject them into the constructor of *any* component or service, *any
|
|||
|
||||
Generally, register providers in the `NgModule` rather than in the root application component.
|
||||
|
||||
我们通常会在 `NgModule` 中注册提供商,而不是在应用程序根组件中。
|
||||
通常会在 `NgModule` 中注册提供商,而不是在应用程序根组件中。
|
||||
|
||||
Do this when you expect the service to be injectable everywhere,
|
||||
or you are configuring another application global service _before the application starts_.
|
||||
|
@ -115,7 +115,7 @@ constructor and the framework takes over.
|
|||
|
||||
The following example shows injecting both the `LoggerService` and the `UserContext` in the `AppComponent`.
|
||||
|
||||
在下列例子中,我们往 `AppComponent` 里注入的 `LoggerService` 和 `UserContext`。
|
||||
下面的例子往 `AppComponent` 里注入的 `LoggerService` 和 `UserContext`。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" title="src/app/app.component.ts" linenums="false">
|
||||
|
||||
|
@ -236,7 +236,7 @@ Accordingly, dependencies provided in the root `AppComponent` can be injected in
|
|||
That isn't always desirable.
|
||||
Sometimes you want to restrict service availability to a particular region of the application.
|
||||
|
||||
但这不一定总是想要的。有时候我们想要把服务的有效性限制到应用程序的一个特定区域。
|
||||
但这不一定总是想要的。有时候你想要把服务的有效性限制到应用程序的一个特定区域。
|
||||
|
||||
You can limit the scope of an injected service to a *branch* of the application hierarchy
|
||||
by providing that service *at the sub-root component for that branch*.
|
||||
|
@ -303,7 +303,7 @@ This is called *sandboxing* because each service and component instance has its
|
|||
|
||||
一个用来保存其伴生组件的实例状态的服务就是个好例子。
|
||||
每个组件都需要该服务的单独实例。
|
||||
每个服务有自己的工作状态,与其它组件的服务和状态隔离。我们称作*沙盒化*,因为每个服务和组件实例都在自己的沙盒里运行。
|
||||
每个服务有自己的工作状态,与其它组件的服务和状态隔离。这叫做*沙箱化*,因为每个服务和组件实例都在自己的沙箱里运行。
|
||||
|
||||
{@a hero-bios-component}
|
||||
|
||||
|
@ -364,7 +364,7 @@ and confirm that the three `HeroBioComponent` instances have their own cached he
|
|||
|
||||
As you now know, dependencies can be registered at any level in the component hierarchy.
|
||||
|
||||
我们知道,依赖可以被注入到任何组件级别。
|
||||
你知道,依赖可以被注入到任何组件级别。
|
||||
|
||||
When a component requests a dependency, Angular starts with that component's injector and walks up the injector tree
|
||||
until it finds the first suitable provider. Angular throws an error if it can't find the dependency during that walk.
|
||||
|
@ -376,7 +376,7 @@ But sometimes you need to limit the search and/or accommodate a missing dependen
|
|||
You can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators,
|
||||
used individually or together.
|
||||
|
||||
大部分时候,我们确实*想要*这个行为。
|
||||
大部分时候,你确实*想要*这个行为。
|
||||
但是有时候,需要限制这个(依赖)查找逻辑,且/或提供一个缺失的依赖。
|
||||
单独或联合使用 `@Host` 和 `@Optional` 限定型装饰器,就可以修改 Angular 的查找行为。
|
||||
|
||||
|
@ -422,7 +422,7 @@ Now there is a new `<hero-contact>` element between the `<hero-bio>` tags.
|
|||
Angular *projects*, or *transcludes*, the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
|
||||
placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
|
||||
|
||||
我们在 `<hero-bio>` 标签中插入了一个新的 `<hero-contact>` 元素。Angular 就会把相应的 `HeroContactComponent`*投影*(*transclude*)进 `HeroBioComponent` 的视图里,
|
||||
在 `<hero-bio>` 标签中是一个新的 `<hero-contact>` 元素。Angular 就会把相应的 `HeroContactComponent`*投影*(*transclude*)进 `HeroBioComponent` 的视图里,
|
||||
将它放在 `HeroBioComponent` 模板的 `<ng-content>` 标签槽里。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bio.component.ts" region="template" title="src/app/hero-bio.component.ts (template)" linenums="false">
|
||||
|
@ -464,7 +464,8 @@ A second `@Host()` function decorates the `loggerService` property.
|
|||
The only `LoggerService` instance in the app is provided at the `AppComponent` level.
|
||||
The host `HeroBioComponent` doesn't have its own `LoggerService` provider.
|
||||
|
||||
另一个 `@Host()` 函数是属性 `loggerService` 的装饰器,我们知道在应用程序中,只有一个 `LoggerService` 实例,也就是在 `AppComponent` 级提供的服务。
|
||||
另一个 `@Host()` 函数是属性 `loggerService` 的装饰器。
|
||||
在本应用程序中只有一个在 `AppComponent` 级提供的 `LoggerService` 实例。
|
||||
该宿主 `HeroBioComponent` 没有自己的 `LoggerService` 提供商。
|
||||
|
||||
Angular would throw an error if you hadn't also decorated the property with the `@Optional()` function.
|
||||
|
@ -514,7 +515,7 @@ require DOM access.
|
|||
To illustrate, here's a simplified version of the `HighlightDirective` from
|
||||
the [Attribute Directives](guide/attribute-directives) page.
|
||||
|
||||
为了说明这一点,我们在[属性型指令](guide/attribute-directives)`HighlightDirective` 的基础上,编写了一个简化版本。
|
||||
要说明这一点,请在[属性型指令](guide/attribute-directives)`HighlightDirective` 的基础上,编写一个简化版。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/highlight.directive.ts" title="src/app/highlight.directive.ts">
|
||||
|
||||
|
@ -556,18 +557,18 @@ The following image shows the effect of mousing over the `<hero-bios-and-contact
|
|||
|
||||
This section demonstrates how to write providers that deliver dependent services.
|
||||
|
||||
在这个部分,我们将演示如何编写提供商来提供被依赖的服务。
|
||||
本节将演示如何编写提供商来提供被依赖的服务。
|
||||
|
||||
Get a service from a dependency injector by giving it a ***token***.
|
||||
|
||||
我们给依赖注入器提供***令牌***来获取服务。
|
||||
给依赖注入器提供***令牌***来获取服务。
|
||||
|
||||
You usually let Angular handle this transaction by specifying a constructor parameter and its type.
|
||||
The parameter type serves as the injector lookup *token*.
|
||||
Angular passes this token to the injector and assigns the result to the parameter.
|
||||
Here's a typical example:
|
||||
|
||||
我们通常在构造函数里面,为参数指定类型,让 Angular 来处理依赖注入。该参数类型就是依赖注入器所需的*令牌*。
|
||||
你通常在构造函数里面,为参数指定类型,让 Angular 来处理依赖注入。该参数类型就是依赖注入器所需的*令牌*。
|
||||
Angular 把该令牌传给注入器,然后把得到的结果赋给参数。下面是一个典型的例子:
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" title="src/app/hero-bios.component.ts (component constructor injection)" linenums="false">
|
||||
|
@ -606,7 +607,7 @@ You have to register your _own_ application providers manually,
|
|||
usually in the `providers` array of the `Component` or `Directive` metadata:
|
||||
|
||||
新建的注入器中没有提供商。
|
||||
Angular 会使用一些自带的提供商来初始化这些注入器。我们必须自行注册属于_自己_的提供商,通常用 ` 组件 ` 或者 ` 指令 ` 元数据中的 `providers` 数组进行注册。
|
||||
Angular 会使用一些自带的提供商来初始化这些注入器。你必须自行注册属于_自己_的提供商,通常用 ` 组件 ` 或者 ` 指令 ` 元数据中的 `providers` 数组进行注册。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="providers" title="src/app/app.component.ts (providers)">
|
||||
|
||||
|
@ -632,7 +633,7 @@ But not every dependency can be satisfied by creating a new instance of a class.
|
|||
You need other ways to deliver dependency values and that means you need other ways to specify a provider.
|
||||
|
||||
注册类提供商之所以这么简单,是因为最常见的可注入服务就是一个类的实例。
|
||||
但是,并不是所有的依赖都只要创建一个类的新实例就可以交付了。我们还需要其它的交付方式,这意味着我们也需要其它方式来指定提供商。
|
||||
但是,并不是所有的依赖都只要创建一个类的新实例就可以交付了。你还需要其它的交付方式,这意味着你也要用其它方式来指定提供商。
|
||||
|
||||
The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them.
|
||||
It's visually simple: a few properties and the logs produced by a logger.
|
||||
|
@ -681,7 +682,7 @@ Use this technique to provide *runtime configuration constants* such as website
|
|||
You can use a *value provider* in a unit test to replace a production service with a fake or mock.
|
||||
|
||||
使用该技巧来进行*运行期常量设置*,比如网站的基础地址和功能标志等。
|
||||
我们通常在单元测试中使用*值-提供商*,用一个假的或模仿的(服务)来取代一个生产环境的服务。
|
||||
你通常在单元测试中使用*值-提供商*,用一个假的或模仿的(服务)来取代一个生产环境的服务。
|
||||
|
||||
The `HeroOfTheMonthComponent` example has two *value providers*.
|
||||
The first provides an instance of the `Hero` class;
|
||||
|
@ -751,7 +752,7 @@ class to be created (`HeroService`) is also the provider's dependency injection
|
|||
It's in this long form to de-mystify the preferred short form.
|
||||
|
||||
第一个提供商是*展开了语法糖的*,是一个典型情况的展开。一般来说,被新建的类(`HeroService`)同时也是该提供商的注入令牌。
|
||||
这里用完整形态来编写它,来反衬我们更喜欢的缩写形式。
|
||||
这里用完整形态来编写它,来反衬更受欢迎的缩写形式。
|
||||
|
||||
The second provider substitutes the `DateLoggerService` for the `LoggerService`.
|
||||
The `LoggerService` is already registered at the `AppComponent` level.
|
||||
|
@ -795,7 +796,7 @@ creating ***two ways to access the same service object***.
|
|||
Narrowing an API through an aliasing interface is _one_ important use case for this technique.
|
||||
The following example shows aliasing for that purpose.
|
||||
|
||||
通过使用别名接口来把一个 API 变窄,是_一个_很重要的该技巧的使用例子。我们在这里就是为了这个目的使用的别名。
|
||||
通过使用别名接口来把一个 API 变窄,是_一个_很重要的该技巧的使用例子。下面的例子中使用别名就是为了这个目的。
|
||||
|
||||
Imagine that the `LoggerService` had a large API, much larger than the actual three methods and a property.
|
||||
You might want to shrink that API surface to just the members you actually need.
|
||||
|
@ -869,7 +870,7 @@ to the "Hero of the Month" contest.
|
|||
The local state is the number `2`, the number of runners-up this component should show.
|
||||
It executes `runnersUpFactory` immediately with `2`.
|
||||
|
||||
本地状态是数字 `2`,该组件应该显示的亚军的个数。我们立刻用 `2` 来执行 `runnersUpFactory`。
|
||||
本地状态是数字 `2`,该组件应该显示的亚军的个数。它就会立刻用 `2` 来执行 `runnersUpFactory`。
|
||||
|
||||
The `runnersUpFactory` itself isn't the provider factory function.
|
||||
The true provider factory function is the function that `runnersUpFactory` returns.
|
||||
|
@ -933,7 +934,7 @@ That's the subject of the next section.
|
|||
The previous *Hero of the Month* example used the `MinimalLogger` class
|
||||
as the token for a provider of a `LoggerService`.
|
||||
|
||||
在前面的*每月英雄*的例子中,我们用了 `MinimalLogger` 类作为 `LoggerService` 提供商的令牌。
|
||||
前面的*月度英雄*的例子使用了 `MinimalLogger` 类作为 `LoggerService` 提供商的令牌。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-existing" title="dependency-injection-in-action/src/app/hero-of-the-month.component.ts">
|
||||
|
||||
|
@ -950,7 +951,7 @@ The `MinimalLogger` is an abstract class.
|
|||
You usually inherit from an abstract class.
|
||||
But *no class* in this application inherits from `MinimalLogger`.
|
||||
|
||||
我们通常从一个抽象类继承。但这个应用中并没有类会继承 `MinimalLogger`。
|
||||
你通常从一个抽象类继承。但这个应用中*并没有*类会继承 `MinimalLogger`。
|
||||
|
||||
The `LoggerService` and the `DateLoggerService` _could_ have inherited from `MinimalLogger`.
|
||||
They could have _implemented_ it instead in the manner of an interface.
|
||||
|
@ -966,7 +967,7 @@ When you use a class this way, it's called a ***class-interface***.
|
|||
The key benefit of a *class-interface* is that you can get the strong-typing of an interface
|
||||
and you can ***use it as a provider token*** in the way you would a normal class.
|
||||
|
||||
我们称这种用法的类叫做*类-接口*。它关键的好处是:提供了接口的强类型,能像正常类一样***把它当做提供商令牌使用***。
|
||||
这种用法的类叫做*类-接口*。它关键的好处是:提供了接口的强类型,能像正常类一样***把它当做提供商令牌使用***。
|
||||
|
||||
A ***class-interface*** should define *only* the members that its consumers are allowed to call.
|
||||
Such a narrowing interface helps decouple the concrete class from its consumers.
|
||||
|
@ -994,7 +995,7 @@ such as a function, an object, a string, or a class.
|
|||
|
||||
Using a class as an interface gives you the characteristics of an interface in a real JavaScript object.
|
||||
|
||||
把类当做接口使用,可以为我们在一个 JavaScript 对象上提供类似于接口的特性。
|
||||
把类当做接口使用,可以为你在一个 JavaScript 对象上提供类似于接口的特性。
|
||||
|
||||
Of course a real object occupies memory. To minimize memory cost, the class should have *no implementation*.
|
||||
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function:
|
||||
|
@ -1091,8 +1092,9 @@ and displays them in the order they arrive from the database.
|
|||
This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server.
|
||||
That's why you call the `HeroService` from within the `ngOnInit` rather than the constructor.
|
||||
|
||||
让构造函数保持简单。它们应该***只***用来初始化变量。这个规则会帮助我们在测试环境中放心的构造组件,以免在构造它们时,无意做了一些非常戏剧化的动作(比如与服务器进行会话)。
|
||||
这就是为什么我们要在 `ngOnInit` 里面调用 `HeroService`,而不是在构造函数中。
|
||||
***让构造函数保持简单。***它们只应该用来初始化变量。
|
||||
这条规则用于在测试环境中放心的构造组件,以免在构造它们时,无意做了一些非常戏剧化的动作(比如与服务器进行会话)。
|
||||
这就是为什么你要在 `ngOnInit` 里面调用 `HeroService`,而不是在构造函数中。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1120,7 +1122,7 @@ But Angular calls the *derived* class's `ngOnInit` *before* calling the base cla
|
|||
so you'd be sorting the heroes array *before they arrived*. That produces a nasty error.
|
||||
|
||||
现在,请注意 `afterGetHeroes()` 方法。
|
||||
我们第一反应是在 `SortedHeroesComponent` 组件里面建一个 `ngOnInit` 方法来做排序。但是 Angular 会先调用*派生*类的 `ngOnInit`,后调用基类的 `ngOnInit`,
|
||||
你的第一反应是在 `SortedHeroesComponent` 组件里面建一个 `ngOnInit` 方法来做排序。但是 Angular 会先调用*派生*类的 `ngOnInit`,后调用基类的 `ngOnInit`,
|
||||
所以可能在*英雄到达之前*就开始排序。这就产生了一个讨厌的错误。
|
||||
|
||||
Overriding the base class's `afterGetHeroes()` method solves the problem.
|
||||
|
@ -1143,7 +1145,7 @@ are preferable. But sometimes it makes sense for one component
|
|||
to have a direct reference to another component
|
||||
perhaps to access values or call methods on that component.
|
||||
|
||||
应用程序组件经常需要共享信息。我们喜欢更加松耦合的技术,比如数据绑定和服务共享。
|
||||
应用程序组件经常需要共享信息。使用松耦合的技术会更好一点,比如数据绑定和服务共享。
|
||||
但有时候组件确实需要拥有另一个组件的引用,用来访问该组件的属性值或者调用它的方法。
|
||||
|
||||
Obtaining a component reference is a bit tricky in Angular.
|
||||
|
@ -1176,7 +1178,7 @@ This section describes some techniques for doing that.
|
|||
|
||||
You use standard class injection to acquire a parent component whose type you know.
|
||||
|
||||
我们使用标准的类注入来获取已知类型的父组件。
|
||||
你使用标准的类注入来获取已知类型的父组件。
|
||||
|
||||
In the following example, the parent `AlexComponent` has several children including a `CathyComponent`:
|
||||
|
||||
|
@ -1202,7 +1204,7 @@ is there for safety,
|
|||
the <live-example name="dependency-injection-in-action"></live-example>
|
||||
confirms that the `alex` parameter is set.
|
||||
|
||||
安全起见,我们添加了[@Optional](guide/dependency-injection-in-action#optional)装饰器,但是<live-example name="dependency-injection-in-action"></live-example>显示 `alex` 参数确实被设置了。
|
||||
注意,这里为安全起见而添加了[@Optional](guide/dependency-injection-in-action#optional)装饰器,<live-example name="dependency-injection-in-action"></live-example>显示 `alex` 参数确实被设置了。
|
||||
|
||||
{@a base-parent}
|
||||
|
||||
|
@ -1234,7 +1236,7 @@ That's not possible because TypeScript interfaces disappear
|
|||
from the transpiled JavaScript, which doesn't support interfaces.
|
||||
There's no artifact to look for.
|
||||
|
||||
更好的方式是通过接口来寻找实现了它的组件。但这是不可能的,因为 TypeScript 的接口在编译成 JavaScript 以后就消失了,JavaScript 不支持接口。我们没有东西可查。
|
||||
更好的方式是通过接口来寻找实现了它的组件。但这是不可能的,因为 TypeScript 的接口在编译成 JavaScript 以后就消失了,JavaScript 不支持接口。没有东西可查。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1247,7 +1249,7 @@ inject its parent via the parent's base class*.
|
|||
The sample's `CraigComponent` explores this question. [Looking back](guide/dependency-injection-in-action#alex),
|
||||
you see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
|
||||
|
||||
`CraigComponent` 例子探究了这个问题。[往回看 Alex]{guide/dependency-injection-in-action#alex},我们看到 `Alex` 组件*扩展*(*派生*)自一个叫 `Base` 的类。
|
||||
`CraigComponent` 例子探究了这个问题。[往回看 Alex]{guide/dependency-injection-in-action#alex},你看到 `Alex` 组件*扩展*(*派生*)自一个叫 `Base` 的类。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" title="parent-finder.component.ts (Alex class signature)" linenums="false">
|
||||
|
||||
|
@ -1292,7 +1294,7 @@ Write an [*alias provider*](guide/dependency-injection-in-action#useexisting)&md
|
|||
definition—that creates an *alternative* way to inject the same component instance
|
||||
and add that provider to the `providers` array of the `@Component` metadata for the `AlexComponent`:
|
||||
|
||||
我们编写一个[*别名提供商*](guide/dependency-injection-in-action#useexisting) &mdash;一个拥有 `useExisting` 定义的 `provide` 函数 —
|
||||
编写一个[*别名提供商*](guide/dependency-injection-in-action#useexisting) &mdash;一个拥有 `useExisting` 定义的 `provide` 函数 —
|
||||
它新建一个*备选的*方式来注入同一个组件实例,并把这个提供商添加到 `AlexComponent` 的 `@Component` 元数据里的 `providers` 数组。
|
||||
|
||||
{@a alex-providers}
|
||||
|
@ -1352,7 +1354,7 @@ Here's *Barry*:
|
|||
If you're going to keep writing [*alias providers*](guide/dependency-injection-in-action#useexisting) like this you should create a [helper function](guide/dependency-injection-in-action#provideparent).
|
||||
|
||||
*Barry* 的 `providers` 数组看起来很像[*Alex* 的那个](guide/dependency-injection-in-action#alex-providers).
|
||||
如果准备一直像这样编写[*别名提供商*](guide/dependency-injection-in-action#useexisting)的话,我们应该建立一个[帮助函数](guide/dependency-injection-in-action#provideparent)。
|
||||
如果准备一直像这样编写[*别名提供商*](guide/dependency-injection-in-action#useexisting)的话,你应该建立一个[辅助函数](guide/dependency-injection-in-action#provideparent)。
|
||||
|
||||
For now, focus on *Barry*'s constructor:
|
||||
|
||||
|
@ -1407,11 +1409,11 @@ Here's *Alice*, *Barry* and family in action:
|
|||
|
||||
You [learned earlier](guide/dependency-injection-in-action#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class.
|
||||
|
||||
我们[以前学过](guide/dependency-injection-in-action#class-interface):*类-接口*是一个抽象类,被当成一个接口使用,而非基类。
|
||||
你[以前学过](guide/dependency-injection-in-action#class-interface):*类-接口*是一个抽象类,被当成一个接口使用,而非基类。
|
||||
|
||||
The example defines a `Parent` *class-interface*.
|
||||
|
||||
我们的例子定义了一个 `Parent`*类-接口*。
|
||||
这个例子定义了一个 `Parent`*类-接口*。
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" title="parent-finder.component.ts (Parent class-interface)" linenums="false">
|
||||
|
||||
|
@ -1485,7 +1487,7 @@ Now you can add a simpler, more meaningful parent provider to your components:
|
|||
You can do better. The current version of the helper function can only alias the `Parent` *class-interface*.
|
||||
The application might have a variety of parent types, each with its own *class-interface* token.
|
||||
|
||||
我们可以做得更好。当前版本的助手函数只能为 `Parent`*类-接口*提供别名。应用程序可能有很多类型的父组件,每个父组件有自己的*类-接口*令牌。
|
||||
你可以做得更好。当前版本的助手函数只能为 `Parent`*类-接口*提供别名。应用程序可能有很多类型的父组件,每个父组件有自己的*类-接口*令牌。
|
||||
|
||||
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*.
|
||||
|
||||
|
@ -1519,8 +1521,8 @@ But sometimes circular references are unavoidable.
|
|||
You're in a bind when class 'A' refers to class 'B' and 'B' refers to 'A'.
|
||||
One of them has to be defined first.
|
||||
|
||||
这通常不是一个问题,特别是当我们遵循*一个文件一个类*规则的时候。
|
||||
但是有时候循环引用可能不能避免。当一个类*A 引用类 B*,同时'B'引用'A'的时候,我们就陷入困境了:它们中间的某一个必须要先定义。
|
||||
这通常不是一个问题,特别是当你遵循*一个文件一个类*规则的时候。
|
||||
但是有时候循环引用可能不能避免。当一个类*A 引用类 B*,同时'B'引用'A'的时候,你就陷入困境了:它们中间的某一个必须要先定义。
|
||||
|
||||
The Angular `forwardRef()` function creates an *indirect* reference that Angular can resolve later.
|
||||
|
||||
|
@ -1535,12 +1537,12 @@ as does the `AlexComponent` in its `providers` array.
|
|||
The `providers` array is a property of the `@Component` decorator function which must
|
||||
appear *above* the class definition.
|
||||
|
||||
当一个类*需要引用自身*的时候,我们面临同样的困境,就像在 `AlexComponent` 的 `provdiers` 数组中遇到的困境一样。
|
||||
当一个类*需要引用自身*的时候,你面临同样的困境,就像在 `AlexComponent` 的 `provdiers` 数组中遇到的困境一样。
|
||||
该 `providers` 数组是一个 `@Component` 装饰器函数的一个属性,它必须在类定义*之前*出现。
|
||||
|
||||
Break the circularity with `forwardRef`:
|
||||
|
||||
我们使用 `forwardRef` 来打破这种循环:
|
||||
使用 `forwardRef` 来打破这种循环:
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" title="parent-finder.component.ts (AlexComponent providers)" linenums="false">
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ That makes `Car` brittle.
|
|||
|
||||
如果 `Engine` 类升级了,它的构造函数要求传入一个参数,这该怎么办?
|
||||
这个 `Car` 类就被破坏了,在把创建引擎的代码重写为 `this.engine = new Engine(theNewParameter)` 之前,它都是坏的。
|
||||
当第一次写 `Car` 类时,我们不关心 `Engine` 构造函数的参数,现在也不想关心。
|
||||
当第一次写 `Car` 类时,你不关心 `Engine` 构造函数的参数,现在也不想关心。
|
||||
但是,当 `Engine` 类的定义发生变化时,就不得不在乎了,`Car` 类也不得不跟着改变。
|
||||
这就会让 `Car` 类过于脆弱。
|
||||
|
||||
|
@ -72,7 +72,7 @@ You're locked into whatever brand the `Tires` class creates. That makes the
|
|||
`Car` class inflexible.
|
||||
|
||||
如果想在 `Car` 上使用不同品牌的轮胎会怎样?太糟了。
|
||||
我们被锁定在 `Tires` 类创建时使用的那个品牌上。这让 `Car` 类缺乏弹性。
|
||||
你被锁定在 `Tires` 类创建时使用的那个品牌上。这让 `Car` 类缺乏弹性。
|
||||
|
||||
Right now each new car gets its own `engine`. It can't share an `engine` with other cars.
|
||||
While that makes sense for an automobile engine,
|
||||
|
@ -82,7 +82,7 @@ to share services that have been created previously for other consumers.
|
|||
|
||||
现在,每辆车都有它自己的引擎。它不能和其它车辆共享引擎。
|
||||
虽然这对于汽车来说还算可以理解,但是设想一下那些应该被共享的依赖,比如用来联系厂家服务中心的车载无线电。
|
||||
我们的车缺乏必要的弹性,无法共享当初给其它消费者创建的车载无线电。
|
||||
这种车缺乏必要的弹性,无法共享当初给其它消费者创建的车载无线电。
|
||||
|
||||
When you write tests for `Car` you're at the mercy of its hidden dependencies.
|
||||
Is it even possible to create a new `Engine` in a test environment?
|
||||
|
@ -94,7 +94,7 @@ You certainly don't want that going on during tests.
|
|||
能在测试环境中成功创建新的 `Engine` 吗?
|
||||
`Engine` 自己又依赖什么?那些依赖本身又依赖什么?
|
||||
`Engine` 的新实例会发起到服务器的异步调用吗?
|
||||
我们当然不想在测试期间这么一层层追下去。
|
||||
你当然不想在测试期间这么一层层追下去。
|
||||
|
||||
What if the `Car` should flash a warning signal when tire pressure is low?
|
||||
How do you confirm that it actually does flash a warning
|
||||
|
@ -106,7 +106,7 @@ if you can't swap in low-pressure tires during the test?
|
|||
You have no control over the car's hidden dependencies.
|
||||
When you can't control the dependencies, a class becomes difficult to test.
|
||||
|
||||
我们没法控制这辆车背后隐藏的依赖。
|
||||
你没法控制这辆车背后隐藏的依赖。
|
||||
当不能控制依赖时,类就会变得难以测试。
|
||||
|
||||
How can you make `Car` more robust, flexible, and testable?
|
||||
|
@ -134,7 +134,7 @@ now in the constructor.
|
|||
The `Car` class no longer creates an `engine` or `tires`.
|
||||
It just consumes them.
|
||||
|
||||
发生了什么?我们把依赖的定义移到了构造函数中。
|
||||
发生了什么?现在依赖的定义移到了构造函数中。
|
||||
`Car` 类不再创建引擎 `engine` 或者轮胎 `tires`。
|
||||
它仅仅“消费”它们。
|
||||
|
||||
|
@ -191,8 +191,8 @@ of its dependencies.
|
|||
You can pass mocks to the constructor that do exactly what you want them to do
|
||||
during each test:
|
||||
|
||||
`Car` 类非常容易测试,因为现在我们对它的依赖有了完全的控制权。
|
||||
在每个测试期间,我们可以往构造函数中传入 mock 对象,做想让它们做的事:
|
||||
`Car` 类非常容易测试,因为现在你对它的依赖有了完全的控制权。
|
||||
在每个测试期间,你可以往构造函数中传入 mock 对象,做想让它们做的事:
|
||||
|
||||
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false">
|
||||
|
||||
|
@ -216,7 +216,7 @@ You need something that takes care of assembling these parts.
|
|||
酷!但是,可怜的消费者怎么办?
|
||||
那些希望得到一个 `Car` 的人们现在必须创建所有这三部分了:`Car`、`Engine` 和 `Tires`。
|
||||
`Car` 类把它的快乐建立在了消费者的痛苦之上。
|
||||
需要某种机制为我们把这三个部分装配好。
|
||||
需要某种机制为你把这三个部分装配好。
|
||||
|
||||
You _could_ write a giant class to do that:
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ Run the <live-example></live-example> anytime.
|
|||
Start by reviewing this simplified version of the _heroes_ feature
|
||||
from the [The Tour of Heroes](tutorial/).
|
||||
|
||||
我们先从[《英雄指南》](tutorial/)中*英雄*特性区的一个简化版本开始。
|
||||
先从[《英雄指南》](tutorial/)中*英雄*特性区的一个简化版本开始。
|
||||
|
||||
<code-tabs>
|
||||
|
||||
|
@ -119,7 +119,7 @@ the `getHeroes` method signature would have to be asynchronous.
|
|||
That's a defect we can safely ignore in this guide where our focus is on
|
||||
_injecting the service_ into the `HeroList` component.
|
||||
|
||||
在这一章我们可以忽略这个问题,因为这里的焦点在于*把服务注入*到 `HeroListComponent` 组件中。
|
||||
我们可以放心地忽略这个问题,因为这里的焦点在于*把服务注入*到 `HeroListComponent` 组件中。
|
||||
|
||||
{@a injector-config}
|
||||
|
||||
|
@ -457,7 +457,7 @@ You'd apply the same *constructor injection* pattern,
|
|||
adding a constructor that takes a `Logger` parameter.
|
||||
|
||||
如果它也有依赖,该怎么办呢?例如,它需要通过日志服务来汇报自己的活动。
|
||||
我们同样用*构造函数注入*模式,来添加一个带有 `Logger` 参数的构造函数。
|
||||
你同样用*构造函数注入*模式,来添加一个带有 `Logger` 参数的构造函数。
|
||||
|
||||
Here is the revised `HeroService` that injects the `Logger`, side-by-side with the previous service for comparison.
|
||||
|
||||
|
@ -687,7 +687,7 @@ Occasionally you'll ask a different class to provide the service.
|
|||
The following code tells the injector
|
||||
to return a `BetterLogger` when something asks for the `Logger`.
|
||||
|
||||
某些时候,我们会请求一个不同的类来提供服务。
|
||||
某些时候,你会请求一个不同的类来提供服务。
|
||||
下列代码告诉注入器,当有人请求 `Logger` 时,返回 `BetterLogger`。
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4" >
|
||||
|
@ -732,12 +732,12 @@ you can't update the old component to use it.
|
|||
|
||||
假设某个旧组件依赖一个 `OldLogger` 类。
|
||||
`OldLogger` 和 `NewLogger` 具有相同的接口,但是由于某些原因,
|
||||
我们不能升级这个旧组件并使用它。
|
||||
你不能升级这个旧组件并使用它。
|
||||
|
||||
When the *old* component logs a message with `OldLogger`,
|
||||
you'd like the singleton instance of `NewLogger` to handle it instead.
|
||||
|
||||
当*旧*组件想使用 `OldLogger` 记录消息时,我们希望改用 `NewLogger` 的单例对象来记录。
|
||||
当*旧*组件想使用 `OldLogger` 记录消息时,你希望改用 `NewLogger` 的单例对象来记录。
|
||||
|
||||
The dependency injector should inject that singleton instance
|
||||
when a component asks for either the new or the old logger.
|
||||
|
@ -749,7 +749,7 @@ The `OldLogger` should be an alias for `NewLogger`.
|
|||
You certainly do not want two different `NewLogger` instances in your app.
|
||||
Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`.
|
||||
|
||||
我们当然不会希望应用中有两个不同的 `NewLogger` 实例。
|
||||
你当然不会希望应用中有两个不同的 `NewLogger` 实例。
|
||||
不幸的是,如果尝试通过 `useClass` 来把 `OldLogger` 作为 `NewLogger` 的别名,就会导致这样的后果。
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
|
||||
|
@ -803,7 +803,7 @@ Sometimes you need to create the dependent value dynamically,
|
|||
based on information you won't have until the last possible moment.
|
||||
Maybe the information changes repeatedly in the course of the browser session.
|
||||
|
||||
有时,我们需要动态创建这个依赖值,因为它所需要的信息直到最后一刻才能确定。
|
||||
有时,你需要动态创建这个依赖值,因为它所需要的信息直到最后一刻才能确定。
|
||||
也许这个信息会在浏览器的会话中不停地变化。
|
||||
|
||||
Suppose also that the injectable service has no independent access to the source of this information.
|
||||
|
@ -849,8 +849,8 @@ Instead, the `HeroService` constructor takes a boolean flag to control display o
|
|||
You can inject the `Logger`, but you can't inject the boolean `isAuthorized`.
|
||||
You'll have to take over the creation of new instances of this `HeroService` with a factory provider.
|
||||
|
||||
我们可以注入 `Logger`,但是不能注入逻辑型的 `isAuthorized`。
|
||||
我们不得不通过通过工厂提供商创建这个 `HeroService` 的新实例。
|
||||
你可以注入 `Logger`,但是不能注入逻辑型的 `isAuthorized`。
|
||||
你不得不通过通过工厂提供商创建这个 `HeroService` 的新实例。
|
||||
|
||||
A factory provider needs a factory function:
|
||||
|
||||
|
@ -894,7 +894,7 @@ Notice that you captured the factory provider in an exported variable, `heroServ
|
|||
This extra step makes the factory provider reusable.
|
||||
You can register the `HeroService` with this variable wherever you need it.
|
||||
|
||||
注意,我们在一个导出的变量中捕获了这个工厂提供商:`heroServiceProvider`。
|
||||
注意,你在一个导出的变量中捕获了这个工厂提供商:`heroServiceProvider`。
|
||||
这个额外的步骤让工厂提供商可被复用。
|
||||
无论哪里需要,都可以使用这个变量注册 `HeroService`。
|
||||
|
||||
|
@ -946,7 +946,7 @@ When you define a constructor parameter with the `HeroService` class type,
|
|||
Angular knows to inject the
|
||||
service associated with that `HeroService` class token:
|
||||
|
||||
编写需要基于类的依赖注入的构造函数对我们来说是很幸运的。
|
||||
编写需要基于类的依赖注入的构造函数对你来说是很幸运的。
|
||||
只要定义一个 `HeroService` 类型的构造函数参数,
|
||||
Angular 就会知道把跟 `HeroService` 类令牌关联的服务注入进来:
|
||||
|
||||
|
@ -984,14 +984,14 @@ They can be object literals such as this one:
|
|||
What if you'd like to make this configuration object available for injection?
|
||||
You know you can register an object with a [value provider](guide/dependency-injection#value-provider).
|
||||
|
||||
我们想让这个配置对象在注入时可用,而且知道可以使用[值提供商](guide/dependency-injection#value-provider)来注册一个对象。
|
||||
如果想让这个配置对象在注入时可用该怎么办?你知道你可以用[值提供商](guide/dependency-injection#value-provider)来注册一个对象。
|
||||
|
||||
But what should you use as the token?
|
||||
You don't have a class to serve as a token.
|
||||
There is no `AppConfig` class.
|
||||
|
||||
但是,这种情况下用什么作令牌呢?
|
||||
我们没办法找一个类来当作令牌,因为没有 `Config` 类。
|
||||
你没办法找一个类来当作令牌,因为没有 `Config` 类。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -1121,8 +1121,8 @@ You can register various kinds of providers,
|
|||
and you know how to ask for an injected object (such as a service) by
|
||||
adding a parameter to a constructor.
|
||||
|
||||
本章,我们学习了 Angular 依赖注入的基础知识。
|
||||
我们可以注册很多种类的提供商,知道如何通过添加构造函数的参数来请求一个注入对象(例如一个服务)。
|
||||
本章,你学习了 Angular 依赖注入的基础知识。
|
||||
你可以注册很多种类的提供商,知道如何通过添加构造函数的参数来请求一个注入对象(例如一个服务)。
|
||||
|
||||
Angular dependency injection is more capable than this guide has described.
|
||||
You can learn more about its advanced features, beginning with its support for
|
||||
|
@ -1142,7 +1142,7 @@ Developers rarely work directly with an injector, but
|
|||
here's an `InjectorComponent` that does.
|
||||
|
||||
这里的 `InjectorComponent` 直接使用了注入器,
|
||||
但我们很少直接使用它。
|
||||
但开发者很少直接使用它。
|
||||
|
||||
<code-example path="dependency-injection/src/app/injector.component.ts" region="injector" title="src/app/injector.component.ts">
|
||||
|
||||
|
@ -1190,7 +1190,7 @@ You're forced to spelunk the implementation to discover what it does.
|
|||
它难以解释、理解和测试。
|
||||
仅通过阅读构造函数,没法知道这个类需要什么或者它将做什么。
|
||||
它可以从任何祖先组件中获得服务,而不仅仅是它自己。
|
||||
会迫使我们深入它的实现,才可能明白它都做了啥。
|
||||
会迫使你深入它的实现,才可能明白它都做了啥。
|
||||
|
||||
Framework developers may take this approach when they
|
||||
must acquire services generically and dynamically.
|
||||
|
@ -1217,7 +1217,7 @@ the `HeroesComponent` in the same file,
|
|||
If you define the component before the service,
|
||||
you'll get a runtime null reference error.
|
||||
|
||||
如果我们蔑视这个建议,并且 —— 比如说 —— 把 `HeroService` 和 `HeroesComponent` 组合在同一个文件里,
|
||||
如果你把 `HeroService` 和 `HeroesComponent` 组合在同一个文件里,
|
||||
**就得把组件定义放在最后面!**
|
||||
如果把组件定义在了服务的前面,
|
||||
在运行时抛出空指针错误。
|
||||
|
|
|
@ -51,7 +51,7 @@ For the simplest deployment, build for development and copy the output directory
|
|||
If you copy to the server's root directory, omit this step and leave the `<base href>` alone.<br><br>
|
||||
Learn more about the role of `<base href>` [below](guide/deployment#base-tag).
|
||||
|
||||
我们会看到在生成的 `dist/index.html` 中 `<base href>` 已经被设置好了。<br><br>
|
||||
你会看到在生成的 `dist/index.html` 中 `<base href>` 已经被设置好了。<br><br>
|
||||
如果复制到服务器的根目录下,就省略这个步骤,并且让 `<base href>` 保持原样。<br><br>
|
||||
要了解 `<base href>` 的作用,参见 [下面](guide/deployment#base-tag) 的内容。
|
||||
|
||||
|
@ -172,7 +172,7 @@ Look at the CLI-generated `main.ts` to see how this works.
|
|||
You can dramatically reduce launch time by only loading the application modules that
|
||||
absolutely must be present when the app starts.
|
||||
|
||||
通过只加载应用启动时必须展示的那些应用模块,我们可以显著缩减启动时间。
|
||||
通过只加载应用启动时必须展示的那些应用模块,你可以显著缩减启动时间。
|
||||
|
||||
Configure the Angular Router to defer loading of all other modules (and their associated code), either by
|
||||
[waiting until the app has launched](guide/router#preloading "Preloading")
|
||||
|
@ -193,7 +193,7 @@ in a file that's eagerly loaded when the app starts, a file such as the root `Ap
|
|||
If you do that, the module will be loaded immediately.
|
||||
|
||||
这是一种常犯的错误。
|
||||
我们本打算惰性加载一个模块,但可能无意中在根模块 `AppModule` 文件中使用一个 JavaScript 的 `import` 语句导入了它。
|
||||
你本打算惰性加载一个模块,但可能无意中在根模块 `AppModule` 文件中使用一个 JavaScript 的 `import` 语句导入了它。
|
||||
这样一来,该模块就被立即加载了。
|
||||
|
||||
The bundling configuration must take lazy loading into consideration.
|
||||
|
@ -204,7 +204,7 @@ You have to create these bundles manually.
|
|||
关于打包(bundle)方式的配置必须考虑到惰性加载问题。
|
||||
因为惰性加载模块不能在 JavaScript 中导入(就像刚才说明的),打包器应该默认排除它们。
|
||||
打包器不知道路由器的配置,并且不会为延迟加载模块创建单独的包。
|
||||
我们不得不手动创建这些包。
|
||||
你不得不手动创建这些包。
|
||||
|
||||
The CLI runs the
|
||||
[Angular Ahead-of-Time Webpack Plugin](https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack)
|
||||
|
@ -224,10 +224,10 @@ The cause may not be what you think it is.
|
|||
You can waste a lot of time and money optimizing something that has no tangible benefit or even makes the app slower.
|
||||
You should measure the app's actual behavior when running in the environments that are important to you.
|
||||
|
||||
如果我们能对“是什么导致了应用变慢”的问题有一个清晰、准确的理解,那就可以对优化什么、如何优化做出更好地决策了。
|
||||
如果你能对“是什么导致了应用变慢”的问题有一个清晰、准确的理解,那就可以对优化什么、如何优化做出更好地决策了。
|
||||
真正的原因可能并不是你所想的那样。
|
||||
我们可能花费大量的时间和金钱去优化一些东西,但它却无法产生可感知的效果甚至让应用变得更慢。
|
||||
我们应该在那些最重要的环境中实际运行,来度量应用的实际行为。
|
||||
你可能花费大量的时间和金钱去优化一些东西,但它却无法产生可感知的效果甚至让应用变得更慢。
|
||||
你应该在那些最重要的环境中实际运行,来度量应用的实际行为。
|
||||
|
||||
The
|
||||
<a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing" title="Chrome DevTools Network Performance">
|
||||
|
@ -334,14 +334,14 @@ See also the [*APP_BASE_HREF*](api/common/APP_BASE_HREF "API: APP_BASE_HREF") al
|
|||
In development, you typically start the server in the folder that holds `index.html`.
|
||||
That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app.
|
||||
|
||||
在开发期间,我们通常会在 `index.html` 所在的目录中启动服务器。这个目录就是根目录,因为 `/` 就是本应用的根,所以我们要在 `index.html` 的顶部添加 `<base href="/">`。
|
||||
在开发期间,你通常会在 `index.html` 所在的目录中启动服务器。这个目录就是根目录,因为 `/` 就是本应用的根,所以我们要在 `index.html` 的顶部添加 `<base href="/">`。
|
||||
|
||||
But on the shared or production server, you might serve the app from a subfolder.
|
||||
For example, when the URL to load the app is something like `http://www.mysite.com/my/app/`,
|
||||
the subfolder is `my/app/` and you should add `<base href="/my/app/">` to the server version of the `index.html`.
|
||||
|
||||
但是在共享服务器或生产服务器上,我们可能得从子目录下启动服务器。
|
||||
比如,当加载本应用的 URL 是 `http://www.mysite.com/my/app/` 时,子目录就是 `my/app/`,而我们就要在服务器版的 `index.html` 中添加 `<base href="/my/app/">`。
|
||||
但是在共享服务器或生产服务器上,你可能得从子目录下启动服务器。
|
||||
比如,当加载本应用的 URL 是 `http://www.mysite.com/my/app/` 时,子目录就是 `my/app/`,而你就要在服务器版的 `index.html` 中添加 `<base href="/my/app/">`。
|
||||
|
||||
When the `base` tag is mis-configured, the app fails to load and the browser console displays `404 - Not Found` errors
|
||||
for the missing files. Look at where it _tried_ to find those files and adjust the base tag appropriately.
|
||||
|
@ -409,7 +409,7 @@ See the [CLI `build` topic](https://github.com/angular/angular-cli/wiki/build) f
|
|||
|
||||
This section covers changes you may have make to the server or to files deployed to the server.
|
||||
|
||||
这一节涵盖了我们对服务器或准备部署到服务器的文件要做的那些修改。
|
||||
这一节涵盖了你可能对服务器或准备部署到服务器的文件要做的那些修改。
|
||||
|
||||
{@a fallback}
|
||||
|
||||
|
@ -422,12 +422,12 @@ You don't need a server-side engine to dynamically compose application pages bec
|
|||
Angular does that on the client-side.
|
||||
|
||||
Angular 应用很适合用简单的静态 HTML 服务器提供服务。
|
||||
我们不需要服务端引擎来动态合成应用页面,因为 Angular 会在客户端完成这件事。
|
||||
你不需要服务端引擎来动态合成应用页面,因为 Angular 会在客户端完成这件事。
|
||||
|
||||
If the app uses the Angular router, you must configure the server
|
||||
to return the application's host page (`index.html`) when asked for a file that it does not have.
|
||||
|
||||
如果该应用使用 Angular 路由器,我们就必须配置服务器,让它对不存在的文件返回应用的宿主页(`index.html`)。
|
||||
如果该应用使用 Angular 路由器,你就必须配置服务器,让它对不存在的文件返回应用的宿主页(`index.html`)。
|
||||
|
||||
{@a deep-link}
|
||||
|
||||
|
@ -459,7 +459,7 @@ But it rejects `http://www.mysite.com/heroes/42` and returns a `404 - Not Found`
|
|||
configured to return `index.html` instead.
|
||||
|
||||
静态服务器会在收到对 `http://www.mysite.com/` 的请求时返回 `index.html`,但是会拒绝对 `http://www.mysite.com/heroes/42` 的请求,
|
||||
并返回一个 `404 - Not Found` 错误,除非,我们把它配置成转而返回 `index.html`。
|
||||
并返回一个 `404 - Not Found` 错误,除非,它被配置成了返回 `index.html`。
|
||||
|
||||
#### Fallback configuration examples
|
||||
|
||||
|
@ -566,7 +566,7 @@ It's also a good idea to
|
|||
and to
|
||||
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
|
||||
|
||||
[GitHub 页面服务](https://pages.github.com/):我们没办法[直接配置](https://github.com/isaacs/github/issues/408) Github 的页面服务,但可以添加一个 404 页,只要把 `index.html` 复制到 `404.html` 就可以了。
|
||||
[GitHub 页面服务](https://pages.github.com/):你没办法[直接配置](https://github.com/isaacs/github/issues/408) Github 的页面服务,但可以添加一个 404 页,只要把 `index.html` 复制到 `404.html` 就可以了。
|
||||
它仍然会给出一个 404 响应,但是浏览器将会正确处理该页,并正常加载该应用。
|
||||
使用[在主分支的 `docs/` 下启动服务](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch)
|
||||
并[创建一个 `.nojekyll` 文件](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)也是一个好办法。
|
||||
|
|
|
@ -109,7 +109,7 @@ the view, such as a keystroke, a timer completion, or a response to an HTTP requ
|
|||
Notice that you don't call **new** to create an instance of the `AppComponent` class.
|
||||
Angular is creating an instance for you. How?
|
||||
|
||||
注意,我们没有调用 **new** 来创建 `AppComponent` 类的实例,是 Angular 替我们创建了它。那么它是如何创建的呢?
|
||||
注意,你没有调用 **new** 来创建 `AppComponent` 类的实例,是 Angular 替我们创建了它。那么它是如何创建的呢?
|
||||
|
||||
The CSS `selector` in the `@Component` decorator specifies an element named `<app-root>`.
|
||||
That element is a placeholder in the body of your `index.html` file:
|
||||
|
@ -125,7 +125,7 @@ When you bootstrap with the `AppComponent` class (in <code>main.ts</code>), Angu
|
|||
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
|
||||
inside the `<app-root>` tag.
|
||||
|
||||
当我们通过 `main.ts` 中的 `AppComponent` 类启动时,Angular 在 `index.html` 中查找一个 `<app-root>` 元素,
|
||||
当你通过 `main.ts` 中的 `AppComponent` 类启动时,Angular 在 `index.html` 中查找一个 `<app-root>` 元素,
|
||||
然后实例化一个 `AppComponent`,并将其渲染到 `<app-root>` 标签中。
|
||||
|
||||
Now run the app. It should display the title and hero name:
|
||||
|
@ -170,7 +170,7 @@ In either style, the template data bindings have the same access to the componen
|
|||
|
||||
By default, the Angular CLI generates components with a template file. You can override that with:
|
||||
|
||||
默认情况下,Angular CLI 生成组件时会带有模板文件,我们可以通过参数覆盖它:
|
||||
默认情况下,Angular CLI 生成组件时会带有模板文件,你可以通过参数覆盖它:
|
||||
|
||||
<code-example hideCopy language="sh" class="code-shell">
|
||||
|
||||
|
@ -339,7 +339,7 @@ That brief syntax does a lot:
|
|||
|
||||
* Initializes that property with the corresponding argument when creating an instance of the class.
|
||||
|
||||
当我们 `new` 出该类的一个实例时,把该属性初始化为相应的参数值。
|
||||
当创建该类的一个实例时,把该属性初始化为相应的参数值。
|
||||
|
||||
### Using the Hero class
|
||||
|
||||
|
@ -452,7 +452,7 @@ Now you know how to use:
|
|||
|
||||
* A TypeScript class to shape the **model data** for your component and display properties of that model.
|
||||
|
||||
用一个 TypeScript 类来为我们的组件描述**模型数据**并显示模型的属性。
|
||||
用一个 TypeScript 类来为你的组件描述**模型数据**并显示模型的属性。
|
||||
|
||||
* **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ to use a template with a static component structure.
|
|||
Instead, you need a way to load a new component without a fixed
|
||||
reference to the component in the ad banner's template.
|
||||
|
||||
我们需要一种新的组件加载方式,它不需要在广告条组件的模板中引用固定的组件。
|
||||
你需要一种新的组件加载方式,它不需要在广告条组件的模板中引用固定的组件。
|
||||
|
||||
Angular comes with its own API for loading components dynamically.
|
||||
|
||||
|
@ -71,7 +71,7 @@ In the `@Directive` decorator, notice the selector name, `ad-host`;
|
|||
that's what you use to apply the directive to the element.
|
||||
The next section shows you how.
|
||||
|
||||
在 `@Directive` 装饰器中,要注意选择器的名称:`ad-host`,它就是我们将应用到元素上的指令。下一节我们会展示如何做。
|
||||
在 `@Directive` 装饰器中,要注意选择器的名称:`ad-host`,它就是你将应用到元素上的指令。下一节会展示该如何做。
|
||||
|
||||
{@a loading-components}
|
||||
|
||||
|
@ -84,7 +84,7 @@ To keep things simple in this example, the HTML is in the `@Component`
|
|||
decorator's `template` property as a template string.
|
||||
|
||||
广告条的大部分实现代码都在 `ad-banner.component.ts` 中。
|
||||
为了让这个例子简单点,我们把 HTML 直接放在了 `@Component` 装饰器的 `template` 属性中。
|
||||
为了让这个例子简单点,HTML 被直接放在了 `@Component` 装饰器的 `template` 属性中。
|
||||
|
||||
The `<ng-template>` element is where you apply the directive you just made.
|
||||
To apply the `AdDirective`, recall the selector from `ad.directive.ts`,
|
||||
|
@ -126,7 +126,7 @@ component.`AdService` returns the actual ads making up the ad campaign.
|
|||
Passing an array of components to `AdBannerComponent` allows for a
|
||||
dynamic list of ads without static elements in the template.
|
||||
|
||||
给 `AdBannerComponent` 传入一个组件数组可以让我们在模板中放入一个广告的动态列表,而不用写死在模板中。
|
||||
给 `AdBannerComponent` 传入一个组件数组可以在模板中放入一个广告的动态列表,而不用写死在模板中。
|
||||
|
||||
With its `getAds()` method, `AdBannerComponent` cycles through the array of `AdItems`
|
||||
and loads a new component every 3 seconds by calling `loadComponent()`.
|
||||
|
@ -141,7 +141,7 @@ The `loadComponent()` method is doing a lot of the heavy lifting here.
|
|||
Take it step by step. First, it picks an ad.
|
||||
|
||||
这里的 `loadComponent()` 方法很重要。
|
||||
我们来一步步看看。首先,它选取了一个广告。
|
||||
来一步步看看。首先,它选取了一个广告。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -175,18 +175,18 @@ exists on this specific instance of the component. How do you know it's
|
|||
this specific instance? Because it's referring to `adHost` and `adHost` is the
|
||||
directive you set up earlier to tell Angular where to insert dynamic components.
|
||||
|
||||
接下来,我们要把 `viewContainerRef` 指向这个组件的现有实例。但我们怎么才能找到这个实例呢?
|
||||
很简单,因为它指向了 `adHost`,而这个 `adHost` 就是我们以前设置过的指令,用来告诉 Angular 该把动态组件插入到什么位置。
|
||||
接下来,你要把 `viewContainerRef` 指向这个组件的现有实例。但我们怎么才能找到这个实例呢?
|
||||
很简单,因为它指向了 `adHost`,而这个 `adHost` 就是你以前设置过的指令,用来告诉 Angular 该把动态组件插入到什么位置。
|
||||
|
||||
As you may recall, `AdDirective` injects `ViewContainerRef` into its constructor.
|
||||
This is how the directive accesses the element that you want to use to host the dynamic component.
|
||||
|
||||
回忆一下,`AdDirective` 曾在它的构造函数中注入了一个 `ViewContainerRef`。
|
||||
因此这个指令可以访问到这个被我们用作动态组件宿主的元素。
|
||||
因此这个指令可以访问到这个你打算用作动态组件宿主的元素。
|
||||
|
||||
To add the component to the template, you call `createComponent()` on `ViewContainerRef`.
|
||||
|
||||
要把这个组件添加到模板中,我们可以调用 `ViewContainerRef` 的 `createComponent()`。
|
||||
要把这个组件添加到模板中,你可以调用 `ViewContainerRef` 的 `createComponent()`。
|
||||
|
||||
The `createComponent()` method returns a reference to the loaded component.
|
||||
Use that reference to interact with the component by assigning to its properties or calling its methods.
|
||||
|
@ -226,7 +226,7 @@ add dynamically loaded components to the `NgModule`'s `entryComponents` array:
|
|||
In the ad banner, all components implement a common `AdComponent` interface to
|
||||
standardize the API for passing data to the components.
|
||||
|
||||
在广告条中,所有组件都实现了一个公共接口 `AdComponent`,它定义了一个标准化的 API,让我们把数据传给组件。
|
||||
在广告条中,所有组件都实现了一个公共接口 `AdComponent`,它定义了一个标准化的 API,来把数据传给组件。
|
||||
|
||||
Here are two sample components and the `AdComponent` interface for reference:
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ It's a primitive start.
|
|||
It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience.
|
||||
All such greatness has humble beginnings.
|
||||
|
||||
在此烹饪宝典中,我们会展示如何利用 `formGroup` 来动态渲染一个简单的表单,包括各种控件类型和验证规则。
|
||||
本文会展示如何利用 `formGroup` 来动态渲染一个简单的表单,包括各种控件类型和验证规则。
|
||||
这个起点很简陋,但可以在这个基础上添加丰富多彩的问卷问题、更优美的渲染以及更卓越的用户体验。
|
||||
|
||||
The example in this cookbook is a dynamic form to build an
|
||||
|
@ -29,7 +29,7 @@ online application experience for heroes seeking employment.
|
|||
The agency is constantly tinkering with the application process.
|
||||
You can create the forms on the fly *without changing the application code*.
|
||||
|
||||
在本例中,我们使用动态表单,为正在找工作的英雄们创建一个在线申请表。英雄管理局会不断修改申请流程,我们要在*不修改应用代码*的情况下,动态创建这些表单。
|
||||
这个例子要为正在找工作的英雄们创建一个在线申请表的动态表单。英雄管理局会不断修改申请流程,你要在*不修改应用代码*的情况下,动态创建这些表单。
|
||||
|
||||
{@a toc}
|
||||
|
||||
|
@ -45,7 +45,7 @@ See the <live-example name="dynamic-form"></live-example>.
|
|||
|
||||
Start by creating an `NgModule` called `AppModule`.
|
||||
|
||||
让我们从创建一个名叫 `AppModule` 的 `NgModule` 开始。
|
||||
从创建一个名叫 `AppModule` 的 `NgModule` 开始。
|
||||
|
||||
This cookbook uses [reactive forms](guide/reactive-forms).
|
||||
|
||||
|
@ -55,11 +55,11 @@ Reactive forms belongs to a different `NgModule` called `ReactiveFormsModule`,
|
|||
so in order to access any reactive forms directives, you have to import
|
||||
`ReactiveFormsModule` from the `@angular/forms` library.
|
||||
|
||||
响应式表单属于另外一个叫做 `ReactiveFormsModule` 的 `NgModule`,所以,为了使用响应式表单类的指令,我们得从 `@angular/forms` 库中引入 `ReactiveFormsModule` 模块。
|
||||
响应式表单属于另外一个叫做 `ReactiveFormsModule` 的 `NgModule`,所以,为了使用响应式表单类的指令,你得从 `@angular/forms` 库中引入 `ReactiveFormsModule` 模块。
|
||||
|
||||
Bootstrap the `AppModule` in `main.ts`.
|
||||
|
||||
我们在 `main.ts` 中启动 `AppModule`。
|
||||
在 `main.ts` 中启动 `AppModule`。
|
||||
|
||||
<code-tabs>
|
||||
|
||||
|
@ -87,7 +87,7 @@ The _question_ is the most fundamental object in the model.
|
|||
|
||||
The following `QuestionBase` is a fundamental question class.
|
||||
|
||||
下面是我们建立的最基础的问卷问题基类,名叫 `QuestionBase`。
|
||||
下面的 `QuestionBase` 是最基础的问卷问题基类。
|
||||
|
||||
<code-example path="dynamic-form/src/app/question-base.ts" title="src/app/question-base.ts">
|
||||
|
||||
|
@ -98,7 +98,7 @@ that represent textbox and dropdown questions.
|
|||
The idea is that the form will be bound to specific question types and render the
|
||||
appropriate controls dynamically.
|
||||
|
||||
在这个基础上,我们派生出两个新类 `TextboxQuestion` 和 `DropdownQuestion`,分别代表文本框和下拉框。这么做的初衷是,表单能动态绑定到特定的问卷问题类型,并动态渲染出合适的控件。
|
||||
在这个基础上,你派生出两个新类 `TextboxQuestion` 和 `DropdownQuestion`,分别代表文本框和下拉框。这么做的初衷是,表单能动态绑定到特定的问卷问题类型,并动态渲染出合适的控件。
|
||||
|
||||
`TextboxQuestion` supports multiple HTML5 types such as text, email, and url
|
||||
via the `type` property.
|
||||
|
@ -121,8 +121,8 @@ Next is `QuestionControlService`, a simple service for transforming the question
|
|||
In a nutshell, the form group consumes the metadata from the question model and
|
||||
allows you to specify default values and validation rules.
|
||||
|
||||
接下来,我们定义了 `QuestionControlService`,一个可以把问卷问题转换为 `FormGroup` 的服务。
|
||||
简而言之,这个 `FormGroup` 使用问卷模型的元数据,并允许我们设置默认值和验证规则。
|
||||
接下来定义了 `QuestionControlService`,一个可以把问卷问题转换为 `FormGroup` 的服务。
|
||||
简而言之,这个 `FormGroup` 使用问卷模型的元数据,并允许你指定默认值和验证规则。
|
||||
|
||||
<code-example path="dynamic-form/src/app/question-control.service.ts" title="src/app/question-control.service.ts" linenums="false">
|
||||
|
||||
|
@ -137,7 +137,7 @@ allows you to specify default values and validation rules.
|
|||
Now that you have defined the complete model you are ready
|
||||
to create components to represent the dynamic form.
|
||||
|
||||
现在我们已经有一个定义好的完整模型了,接着就可以开始创建一个展现动态表单的组件。
|
||||
现在你已经有一个定义好的完整模型了,接着就可以开始创建一个展现动态表单的组件。
|
||||
|
||||
`DynamicFormComponent` is the entry point and the main container for the form.
|
||||
|
||||
|
@ -184,13 +184,13 @@ The `ngSwitch` determines which type of question to display.
|
|||
In both components you're relying on Angular's **formGroup** to connect the template HTML to the
|
||||
underlying control objects, populated from the question model with display and validation rules.
|
||||
|
||||
在这两个组件中,我们依赖 Angular 的 **formGroup** 来把模板 HTML 和底层控件对象连接起来,该对象从问卷问题模型里获取渲染和验证规则。
|
||||
在这两个组件中,你依赖 Angular 的 **formGroup** 来把模板 HTML 和底层控件对象连接起来,该对象从问卷问题模型里获取渲染和验证规则。
|
||||
|
||||
`formControlName` and `formGroup` are directives defined in
|
||||
`ReactiveFormsModule`. The templates can access these directives
|
||||
directly since you imported `ReactiveFormsModule` from `AppModule`.
|
||||
|
||||
`formControlName` 和 `formGroup` 是在 `ReactiveFormsModule` 中定义的指令。我们之所以能在模板中使用它们,是因为我们往 `AppModule` 中导入了 `ReactiveFormsModule`。
|
||||
`formControlName` 和 `formGroup` 是在 `ReactiveFormsModule` 中定义的指令。这个模板之所以能使用它们,是因为你曾从 `AppModule` 中导入了 `ReactiveFormsModule`。
|
||||
|
||||
{@a questionnaire-data}
|
||||
|
||||
|
@ -205,14 +205,14 @@ directly since you imported `ReactiveFormsModule` from `AppModule`.
|
|||
The set of questions you've defined for the job application is returned from the `QuestionService`.
|
||||
In a real app you'd retrieve these questions from storage.
|
||||
|
||||
`QuestionService` 会返回为工作申请表定义的那组问题列表。在真实的应用程序环境中,我们会从数据库里获得这些问题列表。
|
||||
`QuestionService` 会返回为工作申请表定义的那组问题列表。在真实的应用程序环境中,你会从数据库里获得这些问题列表。
|
||||
|
||||
The key point is that you control the hero job application questions
|
||||
entirely through the objects returned from `QuestionService`.
|
||||
Questionnaire maintenance is a simple matter of adding, updating,
|
||||
and removing objects from the `questions` array.
|
||||
|
||||
关键是,我们完全根据 `QuestionService` 返回的对象来控制英雄的工作申请表。
|
||||
关键是,你完全根据 `QuestionService` 返回的对象来控制英雄的工作申请表。
|
||||
要维护这份问卷,只要非常简单的添加、更新和删除 `questions` 数组中的对象就可以了。
|
||||
|
||||
<code-example path="dynamic-form/src/app/question.service.ts" title="src/app/question.service.ts">
|
||||
|
@ -237,7 +237,7 @@ Although in this example you're modelling a job application for heroes, there ar
|
|||
no references to any specific hero question
|
||||
outside the objects returned by `QuestionService`.
|
||||
|
||||
在这个例子中,虽然我们是在为英雄的工作申请表建模,但是除了 `QuestionService` 返回的那些对象外,没有其它任何地方是与英雄有关的。
|
||||
在这个例子中,虽然你是在为英雄的工作申请表建模,但是除了 `QuestionService` 返回的那些对象外,没有其它任何地方是与英雄有关的。
|
||||
|
||||
This is very important since it allows you to repurpose the components for any type of survey
|
||||
as long as it's compatible with the *question* object model.
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
Improve overall data quality by validating user input for accuracy and completeness.
|
||||
|
||||
我们可以通过验证用户输入的准确性和完整性,来增强整体数据质量。
|
||||
通过验证用户输入的准确性和完整性,来增强整体数据质量。
|
||||
|
||||
This page shows how to validate user input in the UI and display useful validation messages
|
||||
using both reactive and template-driven forms. It assumes some basic knowledge of the two
|
||||
forms modules.
|
||||
|
||||
在本烹饪书中,我们展示在界面中如何验证用户输入,并显示有用的验证信息,先使用模板驱动表单方式,再使用响应式表单方式。
|
||||
在本烹饪书中,你展示在界面中如何验证用户输入,并显示有用的验证信息,先使用模板驱动表单方式,再使用响应式表单方式。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -29,7 +29,7 @@ To add validation to a template-driven form, you add the same validation attribu
|
|||
would with [native HTML form validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
|
||||
Angular uses directives to match these attributes with validator functions in the framework.
|
||||
|
||||
为了往模板驱动表单中添加验证机制,我们要添加一些验证属性,就像[原生的 HTML 表单验证器](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)。
|
||||
为了往模板驱动表单中添加验证机制,你要添加一些验证属性,就像[原生的 HTML 表单验证器](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)。
|
||||
Angular 会用指令来匹配这些具有验证功能的指令。
|
||||
|
||||
Every time the value of a form control changes, Angular runs validation and generates
|
||||
|
@ -40,7 +40,7 @@ either a list of validation errors, which results in an INVALID status, or null,
|
|||
You can then inspect the control's state by exporting `ngModel` to a local template variable.
|
||||
The following example exports `NgModel` into a variable called `name`:
|
||||
|
||||
我们可以通过把 `ngModel` 导出成局部模板变量来查看该控件的状态。
|
||||
你可以通过把 `ngModel` 导出成局部模板变量来查看该控件的状态。
|
||||
比如下面这个例子就把 `NgModel` 导出成了一个名叫 `name` 的变量:
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" title="template/hero-form-template.component.html (name)" linenums="false">
|
||||
|
@ -61,7 +61,7 @@ information, see [Custom validators](guide/form-validation#custom-validators) se
|
|||
`FormControl` instance, so you can use this in the template to check for control states such as `valid` and `dirty`. For a full list of control properties, see the [AbstractControl](api/forms/AbstractControl)
|
||||
API reference.
|
||||
|
||||
`#name="ngModel"` 把 `NgModel` 导出成了一个名叫 `name` 的局部变量。`NgModel` 把自己控制的 `FormControl` 实例的属性映射出去,让我们能在模板中检查控件的状态,比如 `valid` 和 `dirty`。要了解完整的控件属性,参见 API 参考手册中的[AbstractControl](api/forms/AbstractControl)。
|
||||
`#name="ngModel"` 把 `NgModel` 导出成了一个名叫 `name` 的局部变量。`NgModel` 把自己控制的 `FormControl` 实例的属性映射出去,让你能在模板中检查控件的状态,比如 `valid` 和 `dirty`。要了解完整的控件属性,参见 API 参考手册中的[AbstractControl](api/forms/AbstractControl)。
|
||||
|
||||
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs`
|
||||
but only if the `name` is invalid and the control is either `dirty` or `touched`.
|
||||
|
@ -84,7 +84,7 @@ The checks for `dirty` and `touched` prevent errors from showing until the user
|
|||
does one of two things: changes the value,
|
||||
turning the control dirty; or blurs the form control element, setting the control to touched.
|
||||
|
||||
我们肯定不希望应用在用户还没有编辑过表单的时候就给他们显示错误提示。
|
||||
你肯定不希望应用在用户还没有编辑过表单的时候就给他们显示错误提示。
|
||||
对 `dirty` 和 `touched` 的检查可以避免这种问题。改变控件的值会改变控件的 `dirty`(脏)状态,而当控件失去焦点时,就会改变控件的 `touched`(碰过)状态。
|
||||
|
||||
</div>
|
||||
|
@ -95,7 +95,7 @@ turning the control dirty; or blurs the form control element, setting the contro
|
|||
|
||||
In a reactive form, the source of truth is the component class. Instead of adding validators through attributes in the template, you add validator functions directly to the form control model in the component class. Angular then calls these functions whenever the value of the control changes.
|
||||
|
||||
在响应式表单中,真正的源码都在组件类中。我们不应该通过模板上的属性来添加验证器,而应该在组件类中直接把验证器函数添加到表单控件模型上(`FormControl`)。然后,一旦控件发生了变化,Angular 就会调用这些函数。
|
||||
在响应式表单中,真正的源码都在组件类中。不应该通过模板上的属性来添加验证器,而应该在组件类中直接把验证器函数添加到表单控件模型上(`FormControl`)。然后,一旦控件发生了变化,Angular 就会调用这些函数。
|
||||
|
||||
### Validator functions
|
||||
|
||||
|
@ -107,13 +107,13 @@ There are two types of validator functions: sync validators and async validators
|
|||
|
||||
* **Sync validators**: functions that take a control instance and immediately return either a set of validation errors or `null`. You can pass these in as the second argument when you instantiate a `FormControl`.
|
||||
|
||||
**同步验证器**函数接受一个控件实例,然后返回一组验证错误或 `null`。我们可以在实例化一个 `FormControl` 时把它作为构造函数的第二个参数传进去。
|
||||
**同步验证器**函数接受一个控件实例,然后返回一组验证错误或 `null`。你可以在实例化一个 `FormControl` 时把它作为构造函数的第二个参数传进去。
|
||||
|
||||
* **Async validators**: functions that take a control instance and return a Promise
|
||||
or Observable that later emits a set of validation errors or `null`. You can
|
||||
pass these in as the third argument when you instantiate a `FormControl`.
|
||||
|
||||
**异步验证器**函数接受一个控件实例,并返回一个承诺(Promise)或可观察对象(Observable),它们稍后会发出一组验证错误或者 `null`。我们可以在实例化一个 `FormControl` 时把它作为构造函数的第三个参数传进去。
|
||||
**异步验证器**函数接受一个控件实例,并返回一个承诺(Promise)或可观察对象(Observable),它们稍后会发出一组验证错误或者 `null`。你可以在实例化一个 `FormControl` 时把它作为构造函数的第三个参数传进去。
|
||||
|
||||
Note: for performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.
|
||||
|
||||
|
@ -126,7 +126,7 @@ Note: for performance reasons, Angular only runs async validators if all sync va
|
|||
You can choose to [write your own validator functions](guide/form-validation#custom-validators), or you can use some of
|
||||
Angular's built-in validators.
|
||||
|
||||
我们可以[写自己的验证器](guide/form-validation#custom-validators),也可以使用一些 Angular 内置的验证器。
|
||||
你可以[写自己的验证器](guide/form-validation#custom-validators),也可以使用一些 Angular 内置的验证器。
|
||||
|
||||
The same built-in validators that are available as attributes in template-driven forms, such as `required` and `minlength`, are all available to use as functions from the `Validators` class. For a full list of built-in validators, see the [Validators](api/forms/Validators) API reference.
|
||||
|
||||
|
@ -135,7 +135,7 @@ The same built-in validators that are available as attributes in template-driven
|
|||
To update the hero form to be a reactive form, you can use some of the same
|
||||
built-in validators—this time, in function form. See below:
|
||||
|
||||
要想把这个英雄表单改造成一个响应式表单,我们还是用那些内置验证器,但这次改为用它们的函数形态。
|
||||
要想把这个英雄表单改造成一个响应式表单,你还是用那些内置验证器,但这次改为用它们的函数形态。
|
||||
|
||||
{@a reactive-component-class}
|
||||
|
||||
|
@ -153,7 +153,7 @@ Note that:
|
|||
|
||||
* As these validators are all sync validators, you pass them in as the second argument.
|
||||
|
||||
由于这些验证器都是同步验证器,因此我们要把它们作为第二个参数传进去。
|
||||
由于这些验证器都是同步验证器,因此你要把它们作为第二个参数传进去。
|
||||
|
||||
* Support multiple validators by passing the functions in as an array.
|
||||
|
||||
|
@ -162,11 +162,11 @@ Note that:
|
|||
* This example adds a few getter methods. In a reactive form, you can always access any form control through the `get` method on its parent group, but sometimes it's useful to define getters as shorthands
|
||||
for the template.
|
||||
|
||||
这个例子添加了一些 getter 方法。在响应式表单中,我们通常会通过它所属的控件组(FormGroup)的 `get` 方法来访问表单控件,但有时候为模板定义一些 getter 作为简短形式。
|
||||
这个例子添加了一些 getter 方法。在响应式表单中,你通常会通过它所属的控件组(FormGroup)的 `get` 方法来访问表单控件,但有时候为模板定义一些 getter 作为简短形式。
|
||||
|
||||
If you look at the template for the name input again, it is fairly similar to the template-driven example.
|
||||
|
||||
如果我们到模板中找到 name 输入框,就会发现它和模板驱动的例子很相似。
|
||||
如果你到模板中找到 name 输入框,就会发现它和模板驱动的例子很相似。
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" title="reactive/hero-form-reactive.component.html (name with error msg)" linenums="false">
|
||||
|
||||
|
@ -184,7 +184,7 @@ Key takeaways:
|
|||
* The `required` attribute is still present. While it's not necessary for validation purposes,
|
||||
you may want to keep it in your template for CSS styling or accessibility reasons.
|
||||
|
||||
`required` 属性仍然存在,虽然验证不再需要它,但我们仍然在模板中保留它,以支持 CSS 样式或可访问性。
|
||||
`required` 属性仍然存在,虽然验证不再需要它,但你仍然要在模板中保留它,以支持 CSS 样式或可访问性。
|
||||
|
||||
## Custom validators
|
||||
|
||||
|
@ -192,7 +192,7 @@ Key takeaways:
|
|||
|
||||
Since the built-in validators won't always match the exact use case of your application, sometimes you'll want to create a custom validator.
|
||||
|
||||
由于内置验证器无法适用于所有应用场景,有时候我们还是得创建自定义验证器。
|
||||
由于内置验证器无法适用于所有应用场景,有时候你还是得创建自定义验证器。
|
||||
|
||||
Consider the `forbiddenNameValidator` function from previous
|
||||
[examples](guide/form-validation#reactive-component-class) in
|
||||
|
@ -223,7 +223,7 @@ and whose value is an arbitrary dictionary of values that you could insert into
|
|||
|
||||
`forbiddenNameValidator` 工厂函数返回配置好的验证器函数。
|
||||
该函数接受一个 Angular 控制器对象,并在控制器值有效时返回 null,或无效时返回验证错误对象。
|
||||
验证错误对象通常有一个名为验证秘钥(`forbiddenName`)的属性。其值为一个任意词典,我们可以用来插入错误信息(`{name}`)。
|
||||
验证错误对象通常有一个名为验证秘钥(`forbiddenName`)的属性。其值为一个任意词典,你可以用来插入错误信息(`{name}`)。
|
||||
|
||||
Custom async validators are similar to sync validators, but they must instead return a Promise or Observable
|
||||
that later emits null or a validation error object. In the case of an Observable, the Observable must complete,
|
||||
|
@ -251,7 +251,7 @@ to the `FormControl`.
|
|||
In template-driven forms, you don't have direct access to the `FormControl` instance, so you can't pass the
|
||||
validator in like you can for reactive forms. Instead, you need to add a directive to the template.
|
||||
|
||||
在模板驱动表单中,我们不用直接访问 `FormControl` 实例。所以我们不能像响应式表单中那样把验证器传进去,而应该在模板中添加一个指令。
|
||||
在模板驱动表单中,你不用直接访问 `FormControl` 实例。所以我们不能像响应式表单中那样把验证器传进去,而应该在模板中添加一个指令。
|
||||
|
||||
The corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `forbiddenNameValidator`.
|
||||
|
||||
|
@ -278,7 +278,7 @@ comes together:
|
|||
|
||||
Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector, `appForbiddenName`, to any input element to activate it. For example:
|
||||
|
||||
一旦 `ForbiddenValidatorDirective` 写好了,我们只要把 `forbiddenName` 选择器添加到输入框上就可以激活这个验证器了。比如:
|
||||
一旦 `ForbiddenValidatorDirective` 写好了,你只要把 `forbiddenName` 选择器添加到输入框上就可以激活这个验证器了。比如:
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" title="template/hero-form-template.component.html (forbidden-name-input)" linenums="false">
|
||||
|
||||
|
@ -303,7 +303,7 @@ doesn’t have a `forbiddenName`.
|
|||
|
||||
Like in AngularJS, Angular automatically mirrors many control properties onto the form control element as CSS classes. You can use these classes to style form control elements according to the state of the form. The following classes are currently supported:
|
||||
|
||||
像 AngularJS 中一样,Angular 会自动把很多控件属性作为 CSS 类映射到控件所在的元素上。我们可以使用这些类来根据表单状态给表单控件元素添加样式。目前支持下列类:
|
||||
像 AngularJS 中一样,Angular 会自动把很多控件属性作为 CSS 类映射到控件所在的元素上。你可以使用这些类来根据表单状态给表单控件元素添加样式。目前支持下列类:
|
||||
|
||||
* `.ng-valid`
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ Forms are the mainstay of business applications.
|
|||
You use forms to log in, submit a help request, place an order, book a flight,
|
||||
schedule a meeting, and perform countless other data-entry tasks.
|
||||
|
||||
表单是商业应用的支柱,我们用它来执行登录、求助、下单、预订机票、安排会议,以及不计其数的其它数据录入任务。
|
||||
表单是商业应用的支柱,你用它来执行登录、求助、下单、预订机票、安排会议,以及不计其数的其它数据录入任务。
|
||||
|
||||
In developing a form, it's important to create a data-entry experience that guides the
|
||||
user efficiently and effectively through the workflow.
|
||||
|
@ -17,7 +17,7 @@ Developing forms requires design skills (which are out of scope for this page),
|
|||
*two-way data binding, change tracking, validation, and error handling*,
|
||||
which you'll learn about on this page.
|
||||
|
||||
开发表单需要设计能力(那超出了本章的范围),而框架支持*双向数据绑定、变更检测、验证和错误处理*,而本章我们会接触到它们。
|
||||
开发表单需要设计能力(那超出了本章的范围),而框架支持*双向数据绑定、变更检测、验证和错误处理*,而本章你将会学到它们。
|
||||
|
||||
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
|
||||
|
||||
|
@ -82,11 +82,11 @@ conditionally enable or disable specific controls, trigger built-in visual feedb
|
|||
Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd
|
||||
otherwise wrestle with yourself.
|
||||
|
||||
它用起来很简单,这是因为 Angular 处理了大多数重复、单调的任务,这让我们可以不必亲自操刀、身陷其中。
|
||||
它用起来很简单,这是因为 Angular 处理了大多数重复、单调的任务,这让你可以不必亲自操刀、身陷其中。
|
||||
|
||||
You'll learn to build a template-driven form that looks like this:
|
||||
|
||||
我们将学习构建如下的“模板驱动”表单:
|
||||
你将学习构建如下的“模板驱动”表单:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
|
||||
|
@ -124,7 +124,7 @@ Note that the *Submit* button is disabled, and the "required" bar to the left of
|
|||
|
||||
You'll build this form in small steps:
|
||||
|
||||
我们将一点点构建出此表单:
|
||||
你将一点点构建出此表单:
|
||||
|
||||
1. Create the `Hero` model class.
|
||||
|
||||
|
@ -272,7 +272,7 @@ Understanding this component requires only the Angular concepts covered in previ
|
|||
|
||||
* The code imports the Angular core library and the `Hero` model you just created.
|
||||
|
||||
这段代码导入了 Angular 核心库以及我们刚刚创建的 `Hero` 模型。
|
||||
这段代码导入了 Angular 核心库以及你刚刚创建的 `Hero` 模型。
|
||||
|
||||
* The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
|
||||
|
||||
|
@ -292,12 +292,12 @@ or perhaps expose these properties as inputs and outputs
|
|||
[Template Syntax](guide/template-syntax) page) for binding to a
|
||||
parent component. This is not a concern now and these future changes won't affect the form.
|
||||
|
||||
接下来,我们可以注入一个数据服务,以获取或保存真实的数据,或者把这些属性暴露为输入属性和输出属性(参见[Template Syntax](guide/template-syntax)中的[输入和输出属性](guide/template-syntax#inputs-outputs))来绑定到一个父组件。这不是现在需要关心的问题,未来的更改不会影响到这个表单。
|
||||
接下来,你可以注入一个数据服务,以获取或保存真实的数据,或者把这些属性暴露为输入属性和输出属性(参见[Template Syntax](guide/template-syntax)中的[输入和输出属性](guide/template-syntax#inputs-outputs))来绑定到一个父组件。这不是现在需要关心的问题,未来的更改不会影响到这个表单。
|
||||
|
||||
* You added a `diagnostic` property to return a JSON representation of the model.
|
||||
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
|
||||
|
||||
我们添加一个 `diagnostic` 属性,以返回这个模型的 JSON 形式。在开发过程中,它用于调试,最后清理时会丢弃它。
|
||||
你添加一个 `diagnostic` 属性,以返回这个模型的 JSON 形式。在开发过程中,它用于调试,最后清理时会丢弃它。
|
||||
|
||||
## Revise *app.module.ts*
|
||||
|
||||
|
@ -405,7 +405,7 @@ You added a *Submit* button at the bottom with some classes on it for styling.
|
|||
|
||||
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
|
||||
|
||||
**我们还没有真正用到 Angular**。没有绑定,没有额外的指令,只有布局。
|
||||
**你还没有真正用到 Angular**。没有绑定,没有额外的指令,只有布局。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -420,8 +420,8 @@ The `container`, `form-group`, `form-control`, and `btn` classes
|
|||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
|
||||
Bootstrap gives the form a little style.
|
||||
|
||||
`container`、`form-group`、`form-control` 和 `btn` 类来自 [Twitter Bootstrap](http://getbootstrap.com/css/)。纯粹是装饰。
|
||||
我们使用 Bootstrap 来美化表单。嘿,一点样式都没有的表单算个啥!
|
||||
`container`、`form-group`、`form-control` 和 `btn` 类来自 [Twitter Bootstrap](http://getbootstrap.com/css/)。这些类纯粹是装饰品。
|
||||
Bootstrap 为这个表单提供了一些样式。
|
||||
|
||||
<div class="callout is-important">
|
||||
|
||||
|
@ -439,7 +439,7 @@ Bootstrap gives the form a little style.
|
|||
|
||||
To add the stylesheet, open `styles.css` and add the following import line at the top:
|
||||
|
||||
我们来添加样式表。打开 `index.html`,并把下列链接添加到 `<head>` 中:
|
||||
要添加样式表,就打开 `index.html`,并把下列链接添加到 `<head>` 中:
|
||||
|
||||
<code-example path="forms/src/styles.1.css" linenums="false" title="src/styles.css">
|
||||
|
||||
|
@ -452,7 +452,7 @@ To add the stylesheet, open `styles.css` and add the following import line at th
|
|||
The hero must choose one superpower from a fixed list of agency-approved powers.
|
||||
You maintain that list internally (in `HeroFormComponent`).
|
||||
|
||||
我们的英雄必须从认证过的固定列表中选择一项超能力。
|
||||
英雄必须从认证过的固定列表中选择一项超能力。
|
||||
这个列表位于 `HeroFormComponent` 中。
|
||||
|
||||
You'll add a `select` to the
|
||||
|
@ -460,7 +460,7 @@ form and bind the options to the `powers` list using `ngFor`,
|
|||
a technique seen previously in the [Displaying Data](guide/displaying-data) page.
|
||||
|
||||
在表单中添加 `select`,用 `ngFor` 把 `powers` 列表绑定到列表选项。
|
||||
我们在之前的[显示数据](guide/displaying-data)一章中见过 `ngFor`。
|
||||
之前的[显示数据](guide/displaying-data)一章中见过 `ngFor`。
|
||||
|
||||
Add the following HTML *immediately below* the *Alter Ego* group:
|
||||
|
||||
|
@ -528,7 +528,7 @@ Find the `<input>` tag for *Name* and update it like this:
|
|||
You left yourself a note to throw it away when you're done.
|
||||
|
||||
在 input 标签后添加用于诊断的插值表达式,以看清正在发生什么事。
|
||||
给自己留个备注,提醒我们完成后移除它。
|
||||
给自己留个备注,提醒你完成后移除它。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -540,7 +540,7 @@ You need one more addition to display the data. Declare
|
|||
a template variable for the form. Update the `<form>` tag with
|
||||
`#heroForm="ngForm"` as follows:
|
||||
|
||||
我们需要更多的工作来显示数据。在表单中声明一个模板变量。往 `<form>` 标签中加入 `#heroForm="ngForm"`,代码如下:
|
||||
你需要更多的工作来显示数据。在表单中声明一个模板变量。往 `<form>` 标签中加入 `#heroForm="ngForm"`,代码如下:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable">
|
||||
|
||||
|
@ -562,7 +562,7 @@ The variable `heroForm` is now a reference to the `NgForm` directive that govern
|
|||
You didn't add an [NgForm](api/forms/NgForm) directive.
|
||||
|
||||
什么是 `NgForm` 指令?
|
||||
但我们明明没有添加过[NgForm](api/forms/NgForm)指令啊!
|
||||
但你明明没有添加过[NgForm](api/forms/NgForm)指令啊!
|
||||
|
||||
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
|
||||
|
||||
|
@ -682,7 +682,7 @@ confirms that all of your changes are reflected in the model.
|
|||
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
|
||||
you if the user touched the control, if the value changed, or if the value became invalid.
|
||||
|
||||
在表单中使用 `ngModel` 可以获得比仅使用双向数据绑定更多的控制权。它还会告诉我们很多信息:用户碰过此控件吗?它的值变化了吗?数据变得无效了吗?
|
||||
在表单中使用 `ngModel` 可以获得比仅使用双向数据绑定更多的控制权。它还会告诉你很多信息:用户碰过此控件吗?它的值变化了吗?数据变得无效了吗?
|
||||
|
||||
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
|
||||
You can leverage those class names to change the appearance of the control.
|
||||
|
@ -836,7 +836,7 @@ The actions and effects are as follows:
|
|||
|
||||
You should see the following transitions and class names:
|
||||
|
||||
我们会看到下列转换及其类名:
|
||||
你会看到下列转换及其类名:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
|
||||
|
@ -846,7 +846,7 @@ The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to se
|
|||
strong visual signal when the values are invalid. You also want to mark required fields.
|
||||
To create such visual feedback, add definitions for the `ng-*` CSS classes.
|
||||
|
||||
(`ng-valid` | `ng-invalid`)这一对是我们最感兴趣的。当数据变得无效时,我们希望发出强力的视觉信号,
|
||||
(`ng-valid` | `ng-invalid`)这一对是你最感兴趣的。当数据变得无效时,我们希望发出强力的视觉信号,
|
||||
还想要标记出必填字段。可以通过加入自定义 CSS 来提供视觉反馈。
|
||||
|
||||
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
|
||||
|
@ -891,7 +891,7 @@ You can improve the form. The _Name_ input box is required and clearing it turns
|
|||
That says something is wrong but the user doesn't know *what* is wrong or what to do about it.
|
||||
Leverage the control's state to reveal a helpful message.
|
||||
|
||||
我们能做的更好。“Name” 输入框是必填的,清空它会让左侧的条变红。这表示*某些东西*是错的,但我们不知道错在哪里,或者如何纠正。
|
||||
你还能做的更好。“Name” 输入框是必填的,清空它会让左侧的条变红。这表示*某些东西*是错的,但我们不知道错在哪里,或者如何纠正。
|
||||
可以借助 `ng-invalid` 类来给出有用的提示。
|
||||
|
||||
When the user deletes the name, the form should look like this:
|
||||
|
@ -916,7 +916,7 @@ To achieve this effect, extend the `<input>` tag with the following:
|
|||
|
||||
Here's an example of an error message added to the _name_ input box:
|
||||
|
||||
这个例子中我们把一条错误信息添加到了_name_输入框中:
|
||||
下面这个例子中把一条错误信息添加到了`name`输入框中:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg">
|
||||
|
||||
|
@ -944,7 +944,7 @@ Here you created a variable called `name` and gave it the value "ngModel".
|
|||
You control visibility of the name error message by binding properties of the `name`
|
||||
control to the message `<div>` element's `hidden` property.
|
||||
|
||||
我们把 `div` 元素的 `hidden` 属性绑定到 `name` 控件的属性,这样就可以控制“姓名”字段错误信息的可见性了。
|
||||
你把 `div` 元素的 `hidden` 属性绑定到 `name` 控件的属性,这样就可以控制“姓名”字段错误信息的可见性了。
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
|
||||
|
||||
|
@ -963,7 +963,7 @@ you'll see the error message immediately, before you've done anything.
|
|||
|
||||
这种用户体验取决于开发人员的选择。有些人会希望任何时候都显示这条消息。
|
||||
如果忽略了 `pristine` 状态,就会只在值有效时隐藏此消息。
|
||||
如果往这个组件中传入全新(空)的英雄,或者无效的英雄,将立刻看到错误信息 —— 虽然我们还啥都没做。
|
||||
如果往这个组件中传入全新(空)的英雄,或者无效的英雄,将立刻看到错误信息 —— 虽然你还啥都没做。
|
||||
|
||||
Some developers want the message to display only when the user makes an invalid change.
|
||||
Hiding the message while the control is "pristine" achieves that goal.
|
||||
|
@ -989,7 +989,7 @@ power to valid values.
|
|||
Now you'll add a new hero in this form.
|
||||
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
|
||||
|
||||
我们希望在这个表单中添加新的英雄。
|
||||
现在,你要在这个表单中添加新的英雄。
|
||||
在表单的底部放置“New Hero(新增英雄)”按钮,并把它的点击事件绑定到 `newHero` 组件。
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" title="src/app/hero-form/hero-form.component.html (New Hero button)">
|
||||
|
@ -1016,20 +1016,20 @@ You don't want error messages when you create a new (empty) hero.
|
|||
Why are you getting one now?
|
||||
|
||||
输入名字,再次点击 *New Hero* 按钮。
|
||||
这次,出现了错误信息!为什么?我们不希望显示新(空)的英雄时,出现错误信息。
|
||||
这次,出现了错误信息!为什么?你不希望显示新(空)的英雄时,出现错误信息。
|
||||
|
||||
Inspecting the element in the browser tools reveals that the *name* input box is _no longer pristine_.
|
||||
The form remembers that you entered a name before clicking *New Hero*.
|
||||
Replacing the hero object *did not restore the pristine state* of the form controls.
|
||||
|
||||
使用浏览器工具审查这个元素就会发现,这个 *name* 输入框并不是全新的。
|
||||
表单记得我们在点击 *New Hero* 前输入的名字。
|
||||
表单记得你在点击 *New Hero* 前输入的名字。
|
||||
更换了英雄对象*并不会重置控件的“全新”状态*。
|
||||
|
||||
You have to clear all of the flags imperatively, which you can do
|
||||
by calling the form's `reset()` method after calling the `newHero()` method.
|
||||
|
||||
我们必须清除所有标记,在调用 `newHero()` 方法后调用表单的 `reset()` 方法即可。
|
||||
你必须清除所有标记,在调用 `newHero()` 方法后调用表单的 `reset()` 方法即可。
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" title="src/app/hero-form/hero-form.component.html (Reset the form)">
|
||||
|
||||
|
@ -1066,14 +1066,14 @@ You'd already defined a template reference variable,
|
|||
`#heroForm`, and initialized it with the value "ngForm".
|
||||
Now, use that variable to access the form with the Submit button.
|
||||
|
||||
我们已经定义了一个模板引用变量 `#heroForm`,并且把赋值为“ngForm”。
|
||||
你已经定义了一个模板引用变量 `#heroForm`,并且把赋值为“ngForm”。
|
||||
现在,就可以在“Submit”按钮中访问这个表单了。
|
||||
|
||||
You'll bind the form's overall validity via
|
||||
the `heroForm` variable to the button's `disabled` property
|
||||
using an event binding. Here's the code:
|
||||
|
||||
我们要把表单的总体有效性通过 `heroForm` 变量绑定到此按钮的 `disabled` 属性上,代码如下:
|
||||
你要把表单的总体有效性通过 `heroForm` 变量绑定到此按钮的 `disabled` 属性上,代码如下:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button">
|
||||
|
||||
|
@ -1088,7 +1088,7 @@ Now if you delete the Name, you violate the "required" rule, which
|
|||
is duly noted in the error message.
|
||||
The *Submit* button is also disabled.
|
||||
|
||||
现在,如果我们删除*姓名*,就会违反“必填姓名”规则,就会像以前那样显示出错误信息。同时,Submit 按钮也被禁用了。
|
||||
现在,如果你删除*姓名*,就会违反“必填姓名”规则,就会像以前那样显示出错误信息。同时,Submit 按钮也被禁用了。
|
||||
|
||||
Not impressed? Think about it for a moment. What would you have to do to
|
||||
wire the button's enable/disabled state to the form's validity without Angular's help?
|
||||
|
@ -1123,7 +1123,7 @@ Submitting the form isn't terribly dramatic at the moment.
|
|||
binding skills.
|
||||
If you aren't interested, skip to this page's conclusion.
|
||||
|
||||
对演示来说,这个收场很平淡的。老实说,即使让它更出彩,也无法教给我们任何关于表单的新知识。
|
||||
对演示来说,这个收场很平淡的。老实说,即使让它更出彩,也无法教给你任何关于表单的新知识。
|
||||
但这是练习新学到的绑定技能的好机会。
|
||||
如果你不感兴趣,可以跳到本章的总结部分。
|
||||
|
||||
|
@ -1149,7 +1149,7 @@ The main form is visible from the start because the
|
|||
as this fragment from the `HeroFormComponent` shows:
|
||||
|
||||
主表单从一开始就是可见的,因为 `submitted` 属性是 false,直到提交了这个表单。
|
||||
来自 `HeroFormComponent` 的代码片段告诉了我们这一点:
|
||||
来自 `HeroFormComponent` 的代码片段证实了这一点:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" title="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted">
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ camelCase is also known as *lower camel case* to distinguish it from *upper came
|
|||
In Angular documentation, "camelCase" always means *lower camel case*.
|
||||
|
||||
这种形式也叫做**小写驼峰式命名法 (lower camel case)**,以区分于**大写驼峰式命名法**,也称 [Pascal 命名法 (PascalCase)](guide/glossary#pascalcase)。
|
||||
在文档中提到“驼峰式命名法 (camelCase) ”的时候,我们所指的都是小驼峰命名法。
|
||||
Angular 文档中提到“驼峰式命名法 (camelCase) ”的时候,所指的都是小驼峰命名法。
|
||||
|
||||
## CLI
|
||||
|
||||
|
@ -263,7 +263,7 @@ Data binding is an alternative to manually pushing application data values into
|
|||
event listeners, pulling changed values from the screen, and
|
||||
updating application data values.
|
||||
|
||||
在数据绑定机制下,我们只要声明一下 HTML 部件和数据源之间的关系,把细节交给框架去处理。
|
||||
在数据绑定机制下,你只要声明一下 HTML 部件和数据源之间的关系,把细节交给框架去处理。
|
||||
而以前的手动操作过程是:将数据推送到 HTML 页面中、添加事件监听器、从屏幕获取变化后的数据,并更新应用中的值。
|
||||
|
||||
Angular has a rich data-binding framework with a variety of data-binding
|
||||
|
@ -382,14 +382,14 @@ that "B" is a dependency of "A."
|
|||
|
||||
这些部件通常会依赖其它部件。一个 Angular [组件 (component)](guide/glossary#component)
|
||||
可能依赖一个服务部件来获取数据或执行运算。
|
||||
如果部件 “A” 要靠另一个部件 “B” 才能工作,我们称 “A” 依赖 “B” ,“B” 是 “A” 的依赖。
|
||||
如果部件 “A” 要靠另一个部件 “B” 才能工作,你就会说 “A” 依赖 “B” ,“B” 是 “A” 的依赖。
|
||||
|
||||
You can ask a "dependency injection system" to create "A"
|
||||
for us and handle all the dependencies.
|
||||
If "A" needs "B" and "B" needs "C," the system resolves that chain of dependencies
|
||||
and returns a fully prepared instance of "A."
|
||||
|
||||
可以要求“依赖注入系统”为我们创建 “A” 并处理所有依赖。如果 “A” 需要 “B” ,“B” 需要 “C ”,
|
||||
你可以要求“依赖注入系统”创建 “A” 并处理所有依赖。如果 “A” 需要 “B” ,“B” 需要 “C ”,
|
||||
系统将解析这个依赖链,返回一个完全准备好的 “A” 实例。
|
||||
|
||||
Angular provides and relies upon its own sophisticated
|
||||
|
@ -410,7 +410,7 @@ methods accept a class name (`Foo`) or a string ("foo") and Angular converts it
|
|||
to a token. When you write `injector.get(Foo)`, the injector returns
|
||||
the value associated with the token for the `Foo` class, typically an instance of `Foo` itself.
|
||||
|
||||
令牌是一个 Angular 中的类型 (`InjectionToken`)。我们很少直接处理令牌。
|
||||
令牌是一个 Angular 中的类型 (`InjectionToken`)。你很少直接处理令牌。
|
||||
绝大多数方法都接受类名 (`Foo`) 或字符串 ("foo"), Angular 会把这些类名称和字符串转换成令牌。
|
||||
当调用 `injector.get(Foo)` 时,注入器返回用 `Foo` 类生成的令牌所对应的依赖值,该依赖值通常是 `Foo` 类的实例。
|
||||
|
||||
|
@ -441,7 +441,7 @@ Angular registers some of its own providers with every injector.
|
|||
You can register your own providers.
|
||||
|
||||
Angular 会为每个注册器注册很多内置提供商。
|
||||
我们也可以注册自己的提供商。
|
||||
你也可以注册自己的提供商。
|
||||
|
||||
Read more in the [Dependency Injection](guide/dependency-injection) page.
|
||||
|
||||
|
@ -465,7 +465,7 @@ A directive is usually associated with an HTML element or attribute.
|
|||
This element or attribute is often referred to as the directive itself.
|
||||
|
||||
指令几乎总与 HTML 元素或属性 (attribute) 相关。
|
||||
我们通常把这些关联到的 HTML 元素或者属性 (attribute) 当做指令本身。
|
||||
通常把这些关联到的 HTML 元素或者属性 (attribute) 当做指令本身。
|
||||
|
||||
When Angular finds a directive in an HTML template,
|
||||
it creates the matching directive class instance
|
||||
|
@ -744,7 +744,7 @@ Angular 应用程序是模块化的。
|
|||
|
||||
In general, you assemble an application from many modules, both the ones you write and the ones you acquire from others.
|
||||
|
||||
一般来说,我们用模块来组装应用程序,这些模块包含自己编写的模块和从其它地方获取的模块。
|
||||
一般来说,你用模块来组装应用程序,这些模块包含自己编写的模块和从其它地方获取的模块。
|
||||
|
||||
A module *exports* something of value in that code, typically one thing such as a class;
|
||||
a module that needs that class *imports* it.
|
||||
|
@ -804,7 +804,7 @@ Observables help you manage asynchronous data, such as data coming from a backen
|
|||
Observables are used within Angular itself, including Angular's event system and its HTTP client service.
|
||||
|
||||
一个 `Observable` 是一个数组,其中的元素随着时间的流逝异步地到达。
|
||||
`Observable` 帮助我们管理异步数据,例如来自后台服务的数据。
|
||||
`Observable` 帮助你管理异步数据,例如来自后台服务的数据。
|
||||
Angular 自身使用了 `Observable`,包括 Angular 的事件系统和它的 http 客户端服务。
|
||||
|
||||
To use observables, Angular uses a third-party library called Reactive Extensions (RxJS).
|
||||
|
@ -869,7 +869,7 @@ Angular 管道是一个函数,用于把输入值转换成输出值以供[视
|
|||
You can also write your own custom pipes.
|
||||
Read more in the page on [pipes](guide/pipes).
|
||||
|
||||
我们还可以写自己的自定义管道。
|
||||
你还可以写自己的自定义管道。
|
||||
更多信息,见[管道](guide/pipes)。
|
||||
|
||||
## Provider
|
||||
|
@ -1027,8 +1027,8 @@ independent from any specific view,
|
|||
provide shared data or logic across components, or encapsulate external interactions.
|
||||
|
||||
服务是一个具有特定功能的类。
|
||||
我们经常创建服务来实现不依赖任何特定视图的特征,
|
||||
在组件之间提供共享数据或逻辑,或者封装外部的交互。
|
||||
你经常创建服务来实现不依赖任何特定视图的特征,
|
||||
在组件之间提供共享数据或逻辑,或者封装外部的交互。
|
||||
|
||||
Applications often require services such as a data service or a logging service.
|
||||
|
||||
|
@ -1192,7 +1192,7 @@ convenient to refer to a component as a view.
|
|||
|
||||
Angular 在一个或多个[指令 (directive)](guide/glossary#directive) 的控制下渲染视图,
|
||||
尤其是[组件 (component)](guide/glossary#component) 指令及其[模板 (template)](guide/glossary#template)。
|
||||
组件扮演着非常重要的角色,我们甚至经常会为了方便, 直接用视图作为组件的代名词。
|
||||
组件扮演着非常重要的角色,以至于习惯上会把组件视为一种视图。
|
||||
|
||||
Views often contain other views. Any view might be loaded and unloaded
|
||||
dynamically as the user navigates through the application, typically
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
You learned the basics of Angular Dependency injection in the
|
||||
[Dependency Injection](guide/dependency-injection) guide.
|
||||
|
||||
在[依赖注入](guide/dependency-injection)一章中,我们已经学过了 Angular 依赖注入的基础知识。
|
||||
在[依赖注入](guide/dependency-injection)一章中,你已经学过了 Angular 依赖注入的基础知识。
|
||||
|
||||
Angular has a _Hierarchical Dependency Injection_ system.
|
||||
There is actually a tree of injectors that parallel an application's component tree.
|
||||
|
@ -13,11 +13,11 @@ You can reconfigure the injectors at any level of that component tree.
|
|||
|
||||
Angular 有一个*多级依赖注入系统*。
|
||||
实际上,应用程序中有一个与组件树平行的注入器树(译注:平行是指结构完全相同且一一对应)。
|
||||
我们可以在组件树中的任何级别上重新配置注入器,达到一些有趣和有用的效果。
|
||||
你可以在组件树中的任何级别上重新配置注入器。
|
||||
|
||||
This guide explores this system and how to use it to your advantage.
|
||||
|
||||
在本章中,我们将浏览这个体系,并告诉你如何善用它。
|
||||
本文将浏览这个体系,并告诉你如何善用它。
|
||||
|
||||
Try the <live-example></live-example>.
|
||||
|
||||
|
@ -30,7 +30,7 @@ Try the <live-example></live-example>.
|
|||
In the [Dependency Injection](guide/dependency-injection) guide,
|
||||
you learned how to configure a dependency injector and how to retrieve dependencies where you need them.
|
||||
|
||||
在[依赖注入](guide/dependency-injection)一章中,我们学过如何配置依赖注入器,以及如何在我们需要时用它获取依赖。
|
||||
在[依赖注入](guide/dependency-injection)一章中,你学过如何配置依赖注入器,以及如何在需要时用它获取依赖。
|
||||
|
||||
In fact, there is no such thing as ***the*** injector.
|
||||
An application may have multiple injectors.
|
||||
|
@ -49,7 +49,7 @@ You won't notice the difference and
|
|||
your mental model should be that every component has its own injector.
|
||||
|
||||
组件的注入器可能是一个组件树中更高级的祖先注入器的*代理*。
|
||||
但这只是提升效率的实现细节,我们不用在乎这点差异,在你的脑海里只要想象成每个组件都有自己的注入器就可以了。
|
||||
但这只是提升效率的实现细节,你不用在乎这点差异,在你的脑海里只要想象成每个组件都有自己的注入器就可以了。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -81,7 +81,7 @@ If it runs out of ancestors, Angular throws an error.
|
|||
当一个组件申请获得一个依赖时,Angular 先尝试用该组件自己的注入器来满足它。
|
||||
如果该组件的注入器没有找到对应的提供商,它就把这个申请转给它父组件的注入器来处理。
|
||||
如果那个注入器也无法满足这个申请,它就继续转给*它的*父组件的注入器。
|
||||
这个申请继续往上冒泡 —— 直到我们找到了一个能处理此申请的注入器或者超出了组件树中的祖先位置为止。
|
||||
这个申请继续往上冒泡 —— 直到找到了一个能处理此申请的注入器或者超出了组件树中的祖先位置为止。
|
||||
如果超出了组件树中的祖先还未找到,Angular 就会抛出一个错误。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
@ -90,9 +90,9 @@ You can cap the bubbling. An intermediate component can declare that it is the "
|
|||
The hunt for providers will climb no higher than the injector for that host component.
|
||||
This is a topic for another day.
|
||||
|
||||
我们还可以“盖住”这次冒泡。一个中层的组件可以声称自己是“宿主”组件。
|
||||
你还可以“盖住”这次冒泡。一个中层的组件可以声称自己是“宿主”组件。
|
||||
向上查找提供商的过程会截止于这个“宿主”组件。
|
||||
我们先保留这个问题,等改天再讨论这个选项。
|
||||
这个问题先放一放,等改天再讨论它。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -104,7 +104,7 @@ You can re-register a provider for a particular dependency token at multiple lev
|
|||
You don't *have* to re-register providers. You shouldn't do so unless you have a good reason.
|
||||
But you *can*.
|
||||
|
||||
我们可以在注入器树中的多个层次上为指定的依赖令牌重新注册提供商。
|
||||
你可以在注入器树中的多个层次上为指定的依赖令牌重新注册提供商。
|
||||
但*并非必须*重新注册,事实上,虽然可以重新注册,但除非有很好的理由,否则不应该这么做。
|
||||
|
||||
As the resolution logic works upwards, the first provider encountered wins.
|
||||
|
@ -118,8 +118,8 @@ It effectively "reconfigures" and "shadows" a provider at a higher level in the
|
|||
If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat.
|
||||
All requests bubble up to the root <code>NgModule</code> injector that you configured with the `bootstrapModule` method.
|
||||
|
||||
如果我们只在顶级(通常是根模块 `AppModule`),这三个注入器看起来将是“平面”的。
|
||||
所有的申请都会冒泡到根<code>NgModule</code>进行处理,也就是我们在 `bootstrapModule` 方法中配置的那个。
|
||||
如果你只在顶级(通常是根模块 `AppModule`),这三个注入器看起来将是“平面”的。
|
||||
所有的申请都会冒泡到根<code>NgModule</code>进行处理,也就是你在 `bootstrapModule` 方法中配置的那个。
|
||||
|
||||
## Component injectors
|
||||
|
||||
|
@ -145,17 +145,17 @@ It gets those villains from a `VillainsService`.
|
|||
While you _could_ provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`),
|
||||
that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows.
|
||||
|
||||
虽然我们也可以在根模块 `AppModule` 中提供 `VillainsService`(就像 `HeroesService` 那样),不过那样一来就会导致在整个应用中到处都能访问到 `VillainsService`,包括在*英雄*工作流中。
|
||||
虽然你也*可以*在根模块 `AppModule` 中提供 `VillainsService`(就像 `HeroesService` 那样),不过那样一来就会导致在整个应用中到处都能访问到 `VillainsService`,包括在*英雄*工作流中。
|
||||
|
||||
If you later modified the `VillainsService`, you could break something in a hero component somewhere.
|
||||
That's not supposed to happen but providing the service in the root `AppModule` creates that risk.
|
||||
|
||||
如果我们以后修改了 `VillainsService`,那就可能会破坏英雄组件中的某些部分。
|
||||
如果你以后修改了 `VillainsService`,那就可能会破坏英雄组件中的某些部分。
|
||||
这可不妙,但是在根模块 `AppModule` 中提供这个服务可能会导致这种风险。
|
||||
|
||||
Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
|
||||
|
||||
我们可以换一种方案:在 `VillainsListComponent` 元数据的 `providers` 中提供 `VillainsService`,就像这样:
|
||||
可以换一种方案:在 `VillainsListComponent` 元数据的 `providers` 中提供 `VillainsService`,就像这样:
|
||||
|
||||
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" title="src/app/villains-list.component.ts (metadata)" region="metadata">
|
||||
|
||||
|
@ -170,7 +170,7 @@ It's still a singleton, but it's a singleton that exist solely in the _villain_
|
|||
|
||||
Now you know that a hero component can't access it. You've reduced your exposure to error.
|
||||
|
||||
现在,我们可以确信英雄组件不会访问它,因此减少了犯错误的机会。
|
||||
现在,你可以确信英雄组件不会访问它,因此减少了犯错误的机会。
|
||||
|
||||
### Scenario: multiple edit sessions
|
||||
|
||||
|
@ -223,7 +223,7 @@ You might delegate that management to a helper service, as this example does.
|
|||
实现方式之一就是让 `HeroTaxReturnComponent` 有逻辑来管理和还原那些更改。
|
||||
这对于简单的报税单来说是很容易的。
|
||||
不过,在现实世界中,报税单的数据模型非常复杂,对这些修改的管理可能不得不投机取巧。
|
||||
于是我们可以把这种管理任务委托给一个辅助服务,就像这个例子中所做的。
|
||||
于是你可以把这种管理任务委托给一个辅助服务,就像这个例子中所做的。
|
||||
|
||||
Here is the `HeroTaxReturnService`.
|
||||
It caches a single `HeroTaxReturn`, tracks changes to that return, and can save or restore it.
|
||||
|
@ -250,7 +250,7 @@ The setter initializes the component's own instance of the `HeroTaxReturnService
|
|||
The getter always returns what that service says is the current state of the hero.
|
||||
The component also asks the service to save and restore this tax return.
|
||||
|
||||
我们通过输入属性得到*要编辑的报税单*,我们把它实现成了读取器(getter)和设置器(setter)。
|
||||
通过输入属性可以得到*要编辑的报税单*,这个属性被实现成了读取器(getter)和设置器(setter)。
|
||||
设置器根据传进来的报税单初始化了组件自己的 `HeroTaxReturnService` 实例。
|
||||
读取器总是返回该服务所存英雄的当前状态。
|
||||
组件也会请求该服务来保存或还原这个报税单。
|
||||
|
@ -285,7 +285,7 @@ No tax return overwriting. No mess.
|
|||
The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation.
|
||||
You can review it and download it from the <live-example></live-example>.
|
||||
|
||||
该场景代码中的其它部分依赖另一些 Angular 的特性和技术,我们将会在本文档的其它章节学到。
|
||||
该场景代码中的其它部分依赖另一些 Angular 的特性和技术,你将会在本文档的其它章节学到。
|
||||
你可以到<live-example></live-example>查看代码和下载它。
|
||||
|
||||
</div>
|
||||
|
@ -304,16 +304,16 @@ Suppose you configured the root injector (marked as A) with _generic_ providers
|
|||
`CarService`, `EngineService` and `TiresService`.
|
||||
|
||||
再次考虑[依赖注入](guide/dependency-injection)一章中车辆(Car)的例子。
|
||||
假设我们在根注入器(代号 A)中配置了*通用的*提供商:`CarService`、`EngineService` 和 `TiresService`。
|
||||
假设你在根注入器(代号 A)中配置了*通用的*提供商:`CarService`、`EngineService` 和 `TiresService`。
|
||||
|
||||
You create a car component (A) that displays a car constructed from these three generic services.
|
||||
|
||||
我们创建了一个车辆组件(A),它显示一个从另外三个通用服务构造出的车辆。
|
||||
你创建了一个车辆组件(A),它显示一个从另外三个通用服务构造出的车辆。
|
||||
|
||||
Then you create a child component (B) that defines its own, _specialized_ providers for `CarService` and `EngineService`
|
||||
that have special capabilites suitable for whatever is going on in component (B).
|
||||
|
||||
然后,我们创建一个子组件(B),它为 `CarService` 和 `EngineService` 定义了自己的*特殊的*提供商,它们具有更特殊的能力,适用于组件 B 的。
|
||||
然后,你创建一个子组件(B),它为 `CarService` 和 `EngineService` 定义了自己的*特殊的*提供商,它们具有更特殊的能力,适用于组件 B 的。
|
||||
|
||||
Component (B) is the parent of another component (C) that defines its own, even _more specialized_ provider for `CarService`.
|
||||
|
||||
|
@ -331,7 +331,7 @@ When you resolve an instance of `Car` at the deepest component (C),
|
|||
its injector produces an instance of `Car` resolved by injector (C) with an `Engine` resolved by injector (B) and
|
||||
`Tires` resolved by the root injector (A).
|
||||
|
||||
当我们在最深层的组件 C 解析 `Car` 的实例时,它使用注入器 C 解析生成了一个 `Car` 的实例,使用注入器 B 解析了 `Engine`,而 `Tires` 则是由根注入器 A 解析的。
|
||||
当你在最深层的组件 C 解析 `Car` 的实例时,它使用注入器 C 解析生成了一个 `Car` 的实例,使用注入器 B 解析了 `Engine`,而 `Tires` 则是由根注入器 A 解析的。
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection/injector-tree.png" alt="car injector tree">
|
||||
|
|
|
@ -202,11 +202,11 @@ easier and safer to consume:
|
|||
|
||||
The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
|
||||
|
||||
响应体可能并不包含我们需要的全部信息。有时候服务器会返回一个特殊的响应头或状态码,以标记出特定的条件,因此读取它们可能是必要的。
|
||||
响应体可能并不包含你需要的全部信息。有时候服务器会返回一个特殊的响应头或状态码,以标记出特定的条件,因此读取它们可能是必要的。
|
||||
|
||||
Tell `HttpClient` that you want the full response with the `observe` option:
|
||||
|
||||
要这样做,我们就要通过 `observe` 选项来告诉 `HttpClient`,你想要完整的响应信息,而不是只有响应体:
|
||||
要这样做,你就要通过 `observe` 选项来告诉 `HttpClient`,你想要完整的响应信息,而不是只有响应体:
|
||||
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
|
@ -1459,7 +1459,7 @@ by returning an observable of simulated events.
|
|||
|
||||
[Cross-Site Request Forgery (XSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website. `HttpClient` supports a [common mechanism](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-Header_Token) used to prevent XSRF attacks. When performing HTTP requests, an interceptor reads a token from a cookie, by default `XSRF-TOKEN`, and sets it as an HTTP header, `X-XSRF-TOKEN`. Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.
|
||||
|
||||
[跨站请求伪造 (XSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery)是一个攻击技术,它能让攻击者假冒一个已认证的用户在你的网站上执行未知的操作。`HttpClient` 支持一种[通用的机制](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-Header_Token)来防范 XSRF 攻击。当执行 HTTP 请求时,一个拦截器会从 cookie 中读取 XSRF 令牌(默认名字为 `XSRF-TOKEN`),并且把它设置为一个 HTTP 头 `X-XSRF-TOKEN`,由于只有运行在我们自己的域名下的代码才能读取这个 cookie,因此后端可以确认这个 HTTP 请求真的来自我们的客户端应用,而不是攻击者。
|
||||
[跨站请求伪造 (XSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery)是一个攻击技术,它能让攻击者假冒一个已认证的用户在你的网站上执行未知的操作。`HttpClient` 支持一种[通用的机制](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-Header_Token)来防范 XSRF 攻击。当执行 HTTP 请求时,一个拦截器会从 cookie 中读取 XSRF 令牌(默认名字为 `XSRF-TOKEN`),并且把它设置为一个 HTTP 头 `X-XSRF-TOKEN`,由于只有运行在你自己的域名下的代码才能读取这个 cookie,因此后端可以确认这个 HTTP 请求真的来自我们的客户端应用,而不是攻击者。
|
||||
|
||||
By default, an interceptor sends this cookie on all mutating requests (POST, etc.)
|
||||
to relative URLs but not on GET/HEAD requests or
|
||||
|
@ -1470,8 +1470,8 @@ on requests with an absolute URL.
|
|||
To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called `XSRF-TOKEN` on either the page load or the first GET request. On subsequent requests the server can verify that the cookie matches the `X-XSRF-TOKEN` HTTP header, and therefore be sure that only code running on your domain could have sent the request. The token must be unique for each user and must be verifiable by the server; this prevents the client from making up its own tokens. Set the token to a digest of your site's authentication
|
||||
cookie with a salt for added security.
|
||||
|
||||
要获得这种优点,我们的服务器需要在页面加载或首个 GET 请求中把一个名叫 `XSRF-TOKEN` 的令牌写入可被 JavaScript 读到的会话 cookie 中。
|
||||
而在后续的请求中,服务器可以验证这个 cookie 是否与 HTTP 头 `X-XSRF-TOKEN` 的值一致,以确保只有运行在我们自己域名下的代码才能发起这个请求。这个令牌必须对每个用户都是唯一的,并且必须能被服务器验证,因此不能由客户端自己生成令牌。把这个令牌设置为你的站点认证信息并且加了盐(salt)的摘要,以提升安全性。
|
||||
要获得这种优点,你的服务器需要在页面加载或首个 GET 请求中把一个名叫 `XSRF-TOKEN` 的令牌写入可被 JavaScript 读到的会话 cookie 中。
|
||||
而在后续的请求中,服务器可以验证这个 cookie 是否与 HTTP 头 `X-XSRF-TOKEN` 的值一致,以确保只有运行在你自己域名下的代码才能发起这个请求。这个令牌必须对每个用户都是唯一的,并且必须能被服务器验证,因此不能由客户端自己生成令牌。把这个令牌设置为你的站点认证信息并且加了盐(salt)的摘要,以提升安全性。
|
||||
|
||||
In order to prevent collisions in environments where multiple Angular apps share the same domain or subdomain, give each application a unique cookie name.
|
||||
|
||||
|
@ -1484,7 +1484,7 @@ Your backend service must be configured to set the cookie for your page, and to
|
|||
the header is present on all eligible requests.
|
||||
If not, Angular's default protection will be ineffective.
|
||||
|
||||
*注意,`HttpClient` 支持的只是 XSRF 防护方案的客户端这一半。* 我们的后端服务必须配置为给页面设置 cookie ,并且要验证请求头,以确保全都是合法的请求。否则,Angular 默认的这种防护措施就会失效。
|
||||
*注意,`HttpClient` 支持的只是 XSRF 防护方案的客户端这一半。* 你的后端服务必须配置为给页面设置 cookie ,并且要验证请求头,以确保全都是合法的请求。否则,Angular 默认的这种防护措施就会失效。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1495,7 +1495,7 @@ If not, Angular's default protection will be ineffective.
|
|||
If your backend service uses different names for the XSRF token cookie or header,
|
||||
use `HttpClientXsrfModule.withOptions()` to override the defaults.
|
||||
|
||||
如果我们的后端服务中对 XSRF 令牌的 cookie 或 头使用了不一样的名字,就要使用 `HttpClientXsrfModule.withConfig()` 来覆盖掉默认值。
|
||||
如果你的后端服务中对 XSRF 令牌的 cookie 或 头使用了不一样的名字,就要使用 `HttpClientXsrfModule.withConfig()` 来覆盖掉默认值。
|
||||
|
||||
<code-example
|
||||
path="http/src/app/app.module.ts"
|
||||
|
@ -1513,7 +1513,7 @@ so your tests can simulate interaction with a remote server.
|
|||
The `@angular/common/http/testing` library makes
|
||||
setting up such mocking straightforward.
|
||||
|
||||
如同所有的外部依赖一样,HTTP 后端也需要在良好的测试实践中被 Mock 掉。`@angular/common/http` 提供了一个测试库 `@angular/common/http/testing`,它让我们可以直截了当的进行这种 Mock 。
|
||||
如同所有的外部依赖一样,HTTP 后端也需要在良好的测试实践中被 Mock 掉。`@angular/common/http` 提供了一个测试库 `@angular/common/http/testing`,它让你可以直截了当的进行这种 Mock 。
|
||||
|
||||
### Mocking philosophy
|
||||
|
||||
|
@ -1606,7 +1606,7 @@ Now you can write a test that expects a GET Request to occur and provides a mock
|
|||
|
||||
The last step, verifying that no requests remain outstanding, is common enough for you to move it into an `afterEach()` step:
|
||||
|
||||
最后一步,验证没有发起过预期之外的请求,足够通用,因此我们可以把它移到 `afterEach()` 中:
|
||||
最后一步,验证没有发起过预期之外的请求,足够通用,因此你可以把它移到 `afterEach()` 中:
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
|
@ -1646,7 +1646,7 @@ It takes the same arguments but returns an array of matching requests.
|
|||
Once returned, these requests are removed from future matching and
|
||||
you are responsible for flushing and verifying them.
|
||||
|
||||
如果我们需要在测试中对重复的请求进行响应,可以使用 `match()` API 来代替 `expectOne()`,它的参数不变,但会返回一个与这些请求相匹配的数组。一旦返回,这些请求就会从将来要匹配的列表中移除,我们要自己验证和刷新(flush)它。
|
||||
如果你需要在测试中对重复的请求进行响应,可以使用 `match()` API 来代替 `expectOne()`,它的参数不变,但会返回一个与这些请求相匹配的数组。一旦返回,这些请求就会从将来要匹配的列表中移除,我们要自己验证和刷新(flush)它。
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
|
|
|
@ -388,7 +388,7 @@ You also can add a meaning, as shown in this example:
|
|||
Be sure to define custom ids that are unique. If you use the same id for two different text messages,
|
||||
only the first one is extracted, and its translation is used in place of both original text messages.
|
||||
|
||||
要确保自定义 id 是唯一的。如果我们对两个*不同的*文本块使用了同一个 id,那么就只有一个会被提取出来,然后其翻译结果会被用于全部原始文本消息。
|
||||
要确保自定义 id 是唯一的。如果你对两个*不同的*文本块使用了同一个 id,那么就只有一个会被提取出来,然后其翻译结果会被用于全部原始文本消息。
|
||||
|
||||
In the example below the custom id `myId` is used for two different messages:
|
||||
|
||||
|
@ -443,8 +443,8 @@ However, if you don't want to create a new DOM element merely to facilitate tran
|
|||
you can wrap the text in an `<ng-container>` element.
|
||||
The `<ng-container>` is transformed into an html comment:
|
||||
|
||||
如果要翻译一段纯文本,我们就可以把它用 `<span>` 标签包裹起来。
|
||||
但如果由于某些原因(比如 CSS 结构方面的考虑),我们可能不希望仅仅为了翻译而创建一个新的 DOM 元素,那么也可以把这段文本包裹进一个 `<ng-container>` 元素中。`<ng-container>` 将被转换成一个 HTML 注释:
|
||||
如果要翻译一段纯文本,你就可以把它用 `<span>` 标签包裹起来。
|
||||
但如果由于某些原因(比如 CSS 结构方面的考虑),你可能不希望仅仅为了翻译而创建一个新的 DOM 元素,那么也可以把这段文本包裹进一个 `<ng-container>` 元素中。`<ng-container>` 将被转换成一个 HTML 注释:
|
||||
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" title="src/app/app.component.html" linenums="false">
|
||||
|
||||
|
@ -632,7 +632,7 @@ The message maps those values to the appropriate translations:
|
|||
|
||||
You can also nest different ICU expressions together, as shown in this example:
|
||||
|
||||
我们也可以把不同的 ICU 表达式嵌套在一起,比如:
|
||||
你也可以把不同的 ICU 表达式嵌套在一起,比如:
|
||||
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-nested" title="src/app/app.component.html">
|
||||
|
||||
|
@ -865,7 +865,7 @@ would enter the translations using an XLIFF file editor.
|
|||
|
||||
This sample file is easy to translate without a special editor or knowledge of French.
|
||||
|
||||
我们不需要任何编辑器或者法语知识就可以轻易的翻译本例子文件。
|
||||
你不需要任何编辑器或者法语知识就可以轻易的翻译本例子文件。
|
||||
|
||||
1. Open `messages.fr.xlf` and find the first `<trans-unit>` section:
|
||||
|
||||
|
@ -1010,7 +1010,7 @@ Here they are together, after translation:
|
|||
A nested expression is similar to the previous examples. As in the previous example, there are
|
||||
two translation units. The first one contains the text outside of the nested expression:
|
||||
|
||||
嵌套的表达式和前一节没有什么不同。就像上一个例子中那样,我们有*两个*翻译单元。
|
||||
嵌套的表达式和前一节没有什么不同。就像上一个例子中那样,这里有*两个*翻译单元。
|
||||
第一个包含嵌套表达式之外的文本:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" title="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
|
|
|
@ -10,8 +10,8 @@ opening an Angular file, reads your `tsconfig.json` file, finds all the
|
|||
templates you have in your application, and then provides language
|
||||
services for any templates that you open.
|
||||
|
||||
Angular 语言服务让我们能在模板内获得自动完成、错误检查、给出提示和内部导航等功能,而不用管这些模板位于外部 HTML 文件中还是内嵌在注解/装饰器的字符串中。
|
||||
Angular 语言服务会自动检测我们要打开的文件(从我们的 `tsconfig.json` 中读取),找出应用中所需的所有模板,然后为我们打开的这些模板提供语言服务。
|
||||
Angular 语言服务让你能在模板内获得自动完成、错误检查、给出提示和内部导航等功能,而不用管这些模板位于外部 HTML 文件中还是内嵌在注解/装饰器的字符串中。
|
||||
Angular 语言服务会自动检测你要打开的文件(从你的 `tsconfig.json` 中读取),找出应用中所需的所有模板,然后为你打开的这些模板提供语言服务。
|
||||
|
||||
## Autocompletion
|
||||
|
||||
|
@ -22,7 +22,7 @@ contextual possibilities and hints as you type. This example shows
|
|||
autocomplete in an interpolation. As you type it out,
|
||||
you can hit tab to complete.
|
||||
|
||||
自动完成可以在输入时为我们提供当前情境下的候选内容和提示,从而提高开发速度。下面这个例子展示了插值表达式中的自动完成功能。当我们进行输入的时候,就可以按 tab 键来自动完成。
|
||||
自动完成可以在输入时为你提供当前情境下的候选内容和提示,从而提高开发速度。下面这个例子展示了插值表达式中的自动完成功能。当你进行输入的时候,就可以按 tab 键来自动完成。
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
|
||||
|
@ -32,7 +32,7 @@ There are also completions within
|
|||
elements. Any elements you have as a component selector will
|
||||
show up in the completion list.
|
||||
|
||||
还有对元素的自动完成。我们定义的任何组件的选择器都会显示在自动完成列表中。
|
||||
还有对元素的自动完成。你定义的任何组件的选择器都会显示在自动完成列表中。
|
||||
|
||||
## Error checking
|
||||
|
||||
|
@ -55,7 +55,7 @@ Navigation allows you to hover to
|
|||
see where a component, directive, module, etc. is from and then
|
||||
click and press F12 to go directly to its definition.
|
||||
|
||||
导航可以让我们在鼠标悬浮时看到某个组件、指令、模块等来自哪里,然后可以点击并按 F12 直接跳转到它的定义处。
|
||||
导航可以让你在鼠标悬浮时看到某个组件、指令、模块等来自哪里,然后可以点击并按 F12 直接跳转到它的定义处。
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
|
||||
|
@ -80,7 +80,7 @@ You can also use the VS Quick Open (⌘+P) to search for the extension. When you
|
|||
enter the following command:
|
||||
|
||||
Visual Studio Code 可以从商店中安装语言服务,这个功能就在左侧菜单面板最底下的那个图标。
|
||||
我们也可以使用 VS 的快速打开(⌘+P)功能来查找这个扩展插件。打开它之后就输入下列命令:
|
||||
你也可以使用 VS 的快速打开(⌘+P)功能来查找这个扩展插件。打开它之后就输入下列命令:
|
||||
|
||||
```sh
|
||||
|
||||
|
@ -101,8 +101,8 @@ When Angular sees this dev dependency, it provides the
|
|||
language service inside of WebStorm. Webstorm then gives you
|
||||
colorization inside the template and autocomplete in addition to the Angular Language Service.
|
||||
|
||||
在 WebStorm 中,我们必须把语言服务安装为一个开发依赖。
|
||||
当 Angular 看到这个开发依赖时,它就会在 WebStorm 中提供语言服务。除了 Angular 语言服务之外,WebStorm 还会为我们提供模板中的代码高亮和自动完成功能。
|
||||
在 WebStorm 中,你必须把语言服务安装为一个开发依赖。
|
||||
当 Angular 看到这个开发依赖时,它就会在 WebStorm 中提供语言服务。除了 Angular 语言服务之外,WebStorm 还会为你提供模板中的代码高亮和自动完成功能。
|
||||
|
||||
Here's the dev dependency
|
||||
you need to have in `package.json`:
|
||||
|
@ -155,7 +155,7 @@ yarn install
|
|||
In [Sublime Text](https://www.sublimetext.com/), you first need an extension to allow Typescript.
|
||||
Install the latest version of typescript in a local `node_modules` directory:
|
||||
|
||||
在[Sublime Text](https://www.sublimetext.com/)中,我们首先需要一个扩展来支持 TypeScript。
|
||||
在[Sublime Text](https://www.sublimetext.com/)中,你首先需要一个扩展来支持 TypeScript。
|
||||
把最新版本的 TypeScript 安装到本地的 `node_modules` 目录下:
|
||||
|
||||
```sh
|
||||
|
@ -195,7 +195,7 @@ Next, in your user preferences (`Cmd+,` or `Ctrl+,`), add:
|
|||
You can also install Angular Language Service in your project with the
|
||||
following `npm` command:
|
||||
|
||||
我们还可以使用下列 `npm` 命令来把 Angular 语言服务安装到项目中:
|
||||
你还可以使用下列 `npm` 命令来把 Angular 语言服务安装到项目中:
|
||||
|
||||
```sh
|
||||
|
||||
|
@ -220,7 +220,7 @@ Note that this only provides diagnostics and completions in `.ts`
|
|||
files. You need a custom sublime plugin (or modifications to the current plugin)
|
||||
for completions in HTML files.
|
||||
|
||||
注意,这只是提供了 `.ts` 文件中的诊断与自动完成。我们需要一个自定义的 sublime 插件(或修改现有插件)来在 HTML 文件中提供自动完成功能。
|
||||
注意,这只是提供了 `.ts` 文件中的诊断与自动完成。你需要一个自定义的 sublime 插件(或修改现有插件)来在 HTML 文件中提供自动完成功能。
|
||||
|
||||
## How the Language Service works
|
||||
|
||||
|
@ -235,12 +235,12 @@ what module the template is part of, the scope you're in, and the component sele
|
|||
context, it can then determine what the children can be.
|
||||
|
||||
当使用带有语言服务的编辑器时,就会有一个编辑器进程,它会启动一个独立的语言服务进程/服务,它们通过[RPC](https://en.wikipedia.org/wiki/Remote_procedure_call)彼此交谈。
|
||||
当我们在编辑器中输入的时候,它把这些信息发送到另一个进程中,以便追踪项目的状态。
|
||||
当我们在模板中触发一个自动完成列表时,编辑器进程就会先把这个模板解析成 HTML AST,或者叫[抽象语法树](https://en.wikipedia.org/wiki/Abstract_syntax_tree)。然后,Angular 编译器就会解释模板所属的模块以及模板选择器。然后它找出我们的光标目前正在模板 AST 的什么位置。一旦它确定了情境,就可以决定其子节点可以是什么了。
|
||||
当你在编辑器中输入的时候,它把这些信息发送到另一个进程中,以便追踪项目的状态。
|
||||
当你在模板中触发一个自动完成列表时,编辑器进程就会先把这个模板解析成 HTML AST,或者叫[抽象语法树](https://en.wikipedia.org/wiki/Abstract_syntax_tree)。然后,Angular 编译器就会解释模板所属的模块以及模板选择器。然后它找出光标目前正在模板 AST 的什么位置。一旦它确定了情境,就可以决定其子节点可以是什么了。
|
||||
|
||||
It's a little more involved if you are in an interpolation. If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". That's when the template parser produces an expression AST, which resides within the template AST. The Angular Language Services then looks at `data.---` within its context and asks the TypeScript Language Service what the members of data are. TypeScript then returns the list of possibilities.
|
||||
|
||||
如果是在插值表达式中,还会牵扯到更多东西。如果我们在 `div` 元素中有一个插值表达式 `{{data.---}}`,并且需要在输入了 `data.` 之后提供自动完成列表,编译器就没办法使用 HTML AST 来找出答案了。
|
||||
如果是在插值表达式中,还会牵扯到更多东西。如果你在 `div` 元素中有一个插值表达式 `{{data.---}}`,并且需要在输入了 `data.` 之后提供自动完成列表,编译器就没办法使用 HTML AST 来找出答案了。
|
||||
HTML AST 只能告诉编译器,有一些具有 "`{{data.---}}`" 特征的文本。也就是说模板解析器会生成表达式的 AST ,并且放在模板的 AST 中。Angular 语言服务然后在这个情境下查找 `data.---`,并向 TypeScript 语言服务询问这些数据都有哪些成员。然后 TypeScript 就会返回一个可能的列表。
|
||||
|
||||
For more in-depth information, see the
|
||||
|
|
|
@ -14,7 +14,7 @@ Angular 创建它,渲染它,创建并渲染它的子组件,在它被绑定
|
|||
Angular offers **lifecycle hooks**
|
||||
that provide visibility into these key life moments and the ability to act when they occur.
|
||||
|
||||
Angular 提供了**生命周期钩子**,把这些关键生命时刻暴露出来,赋予我们在它们发生时采取行动的能力。
|
||||
Angular 提供了**生命周期钩子**,把这些关键生命时刻暴露出来,赋予你在它们发生时采取行动的能力。
|
||||
|
||||
A directive has the same set of lifecycle hooks.
|
||||
|
||||
|
@ -279,18 +279,18 @@ Fortunately, they aren't necessary.
|
|||
You don't have to add the lifecycle hook interfaces to directives and components to benefit from the hooks themselves.
|
||||
|
||||
幸运的是,它们并不是必须的。
|
||||
我们并不需要在指令和组件上添加生命周期钩子接口就能获得钩子带来的好处。
|
||||
你并不需要在指令和组件上添加生命周期钩子接口就能获得钩子带来的好处。
|
||||
|
||||
Angular instead inspects directive and component classes and calls the hook methods *if they are defined*.
|
||||
Angular finds and calls methods like `ngOnInit()`, with or without the interfaces.
|
||||
|
||||
Angular 会去检测我们的指令和组件的类,一旦发现钩子方法被定义了,就调用它们。
|
||||
Angular 会去检测这些指令和组件的类,一旦发现钩子方法被定义了,就调用它们。
|
||||
Angular 会找到并调用像 `ngOnInit()` 这样的钩子方法,有没有接口无所谓。
|
||||
|
||||
Nonetheless, it's good practice to add interfaces to TypeScript directive classes
|
||||
in order to benefit from strong typing and editor tooling.
|
||||
|
||||
虽然如此,我们还是强烈建议你在 TypeScript 指令类中添加接口,以获得强类型和 IDE 等编辑器带来的好处。
|
||||
虽然如此,在 TypeScript 指令类中添加接口是一项最佳实践,它可以获得强类型和 IDE 等编辑器带来的好处。
|
||||
|
||||
{@a other-lifecycle-hooks}
|
||||
|
||||
|
@ -305,7 +305,7 @@ Angular 的其它子系统除了有这些组件钩子外,还可能有它们自
|
|||
3rd party libraries might implement their hooks as well in order to give developers more
|
||||
control over how these libraries are used.
|
||||
|
||||
第三方库也可能会实现它们自己的钩子,以便让我们这些开发者在使用时能做更多的控制。
|
||||
第三方库也可能会实现它们自己的钩子,以便让这些开发者在使用时能做更多的控制。
|
||||
|
||||
{@a the-sample}
|
||||
|
||||
|
@ -383,7 +383,7 @@ Here's a brief description of each exercise:
|
|||
A `SpyDirective` can log when the element it spies upon is
|
||||
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
||||
|
||||
指令也同样有生命周期钩子。我们新建了一个 `SpyDirective`,利用 `ngOnInit` 和 `ngOnDestroy` 钩子,在它所监视的每个元素被创建或销毁时输出日志。
|
||||
指令也同样有生命周期钩子。`SpyDirective` 可以利用 `ngOnInit` 和 `ngOnDestroy` 钩子在它所监视的每个元素被创建或销毁时输出日志。
|
||||
|
||||
This example applies the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
||||
managed by the parent `SpyComponent`.
|
||||
|
@ -499,7 +499,7 @@ Here's a brief description of each exercise:
|
|||
to the `CounterComponent` log where it watches log entries being created and destroyed.
|
||||
|
||||
在这个例子中,每当父组件递增它的输入属性 `counter` 时,`CounterComponent` 就会通过 `ngOnChanges` 记录一条变更。
|
||||
同时,我们还把前一个例子中的 `SpyDirective` 用在 `CounterComponent` 上,来提供日志,可以同时观察到日志的创建和销毁过程。
|
||||
同时,前一个例子中的 `SpyDirective` 被用于在 `CounterComponent` 上提供日志,它可以同时观察到日志的创建和销毁过程。
|
||||
|
||||
</td>
|
||||
|
||||
|
@ -509,7 +509,7 @@ Here's a brief description of each exercise:
|
|||
|
||||
The remainder of this page discusses selected exercises in further detail.
|
||||
|
||||
接下来,我们将详细讨论这些练习。
|
||||
本文剩下的部分将详细讨论这些练习。
|
||||
|
||||
{@a peek-a-boo}
|
||||
|
||||
|
@ -525,7 +525,7 @@ You would rarely, if ever, implement all of the interfaces like this.
|
|||
The peek-a-boo exists to show how Angular calls the hooks in the expected order.
|
||||
|
||||
你可能很少、或者永远不会像这里一样实现所有这些接口。
|
||||
我们之所以在 peek-a-boo 中这么做,只是为了观看 Angular 是如何按照期望的顺序调用这些钩子的。
|
||||
之所以在 peek-a-boo 中这么做,是为了演示 Angular 是如何按照期望的顺序调用这些钩子的。
|
||||
|
||||
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
|
||||
|
||||
|
@ -557,8 +557,8 @@ Had the user clicked the *Update Hero* button, the log would show another `OnCha
|
|||
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
||||
Clearly these three hooks fire *often*. Keep the logic in these hooks as lean as possible!
|
||||
|
||||
如果我们点击*Update Hero*按钮,就会看到另一个 `OnChanges` 和至少两组 `DoCheck`、`AfterContentChecked` 和 `AfterViewChecked` 钩子。
|
||||
显然,这三种钩子被触发了*很多次*,所以我们必须让这三种钩子里的逻辑尽可能的精简!
|
||||
如果用户点击*Update Hero*按钮,就会看到另一个 `OnChanges` 和至少两组 `DoCheck`、`AfterContentChecked` 和 `AfterViewChecked` 钩子。
|
||||
显然,这三种钩子被触发了*很多次*,必须让这三种钩子里的逻辑尽可能的精简!
|
||||
|
||||
The next examples focus on hook details.
|
||||
|
||||
|
@ -577,7 +577,7 @@ Go undercover with these two spy hooks to discover when an element is initialize
|
|||
This is the perfect infiltration job for a directive.
|
||||
The heroes will never know they're being watched.
|
||||
|
||||
指令是一种完美的渗透方式,我们的英雄永远不会知道该指令的存在。
|
||||
指令是一种完美的渗透方式,这些英雄们永远不会知道该指令的存在。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -594,17 +594,17 @@ The heroes will never know they're being watched.
|
|||
You can't modify a third party component either.
|
||||
But you can watch both with a directive.
|
||||
|
||||
一个侦探(spy)指令可以让我们在无法直接修改 DOM 对象实现代码的情况下,透视其内部细节。
|
||||
一个侦探(spy)指令可以让你在无法直接修改 DOM 对象实现代码的情况下,透视其内部细节。
|
||||
显然,你不能修改一个原生 `<div>` 元素的实现代码。
|
||||
你同样不能修改第三方组件。
|
||||
但我们用一个指令就能监视它们了。
|
||||
但你用一个指令就能监视它们了。
|
||||
|
||||
</div>
|
||||
|
||||
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` and `ngOnDestroy()` hooks
|
||||
that log messages to the parent via an injected `LoggerService`.
|
||||
|
||||
我们这个鬼鬼祟祟的侦探指令很简单,几乎完全由 `ngOnInit()` 和 `ngOnDestroy()` 钩子组成,它通过一个注入进来的 `LoggerService` 来把消息记录到父组件中去。
|
||||
这个鬼鬼祟祟的侦探指令很简单,几乎完全由 `ngOnInit()` 和 `ngOnDestroy()` 钩子组成,它通过一个注入进来的 `LoggerService` 来把消息记录到父组件中去。
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" title="src/app/spy.directive.ts" linenums="false"></code-example>
|
||||
|
||||
|
@ -612,7 +612,7 @@ You can apply the spy to any native or component element and it'll be initialize
|
|||
at the same time as that element.
|
||||
Here it is attached to the repeated hero `<div>`:
|
||||
|
||||
我们可以把这个侦探指令写到任何原生元素或组件元素上,它将与所在的组件同时初始化和销毁。
|
||||
你可以把这个侦探指令写到任何原生元素或组件元素上,它将与所在的组件同时初始化和销毁。
|
||||
下面是把它附加到用来重复显示英雄数据的这个 `<div>` 上。
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" title="src/app/spy.component.html" linenums="false"></code-example>
|
||||
|
@ -680,7 +680,7 @@ created under test or before you decide to display it.
|
|||
Constructors should do no more than set the initial local variables to simple values.
|
||||
|
||||
不要在组件的构造函数中获取数据?
|
||||
在测试环境下新建组件时或在我们决定显示它之前,我们不应该担心它会尝试联系远程服务器。
|
||||
在测试环境下新建组件时或在你决定要显示它之前,不应该担心它会尝试联系远程服务器。
|
||||
构造函数中除了使用简单的值对局部变量进行初始化之外,什么都不应该做。
|
||||
|
||||
An `ngOnInit()` is a good place for a component to fetch its initial data. The
|
||||
|
@ -693,7 +693,7 @@ That's a problem if you need to initialize the directive based on those properti
|
|||
They'll have been set when `ngOnInit()` runs.
|
||||
|
||||
另外还要记住,在指令的_构造函数完成之前_,那些被绑定的输入属性还都没有值。
|
||||
如果我们需要基于这些属性的值来初始化这个指令,这种情况就会出问题。
|
||||
如果你需要基于这些属性的值来初始化这个指令,这种情况就会出问题。
|
||||
而当 `ngOnInit()` 执行的时候,这些属性都已经被正确的赋值过了。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
@ -702,7 +702,7 @@ They'll have been set when `ngOnInit()` runs.
|
|||
Angular calls `ngOnChanges()` before `ngOnInit()` and many times after that.
|
||||
It only calls `ngOnInit()` once.
|
||||
|
||||
我们访问这些属性的第一次机会,实际上是 `ngOnChanges()` 方法,Angular 会在 `ngOnInit()` 之前调用它。
|
||||
`ngOnChanges()` 方法是你访问这些属性的第一次机会,Angular 会在 `ngOnInit()` 之前调用它。
|
||||
但是在那之后,Angular 还会调用 `ngOnChanges()` 很多次。而 `ngOnInit()` 只会被调用一次。
|
||||
|
||||
</div>
|
||||
|
@ -755,7 +755,7 @@ The `ngOnChanges()` method takes an object that maps each changed property name
|
|||
This hook iterates over the changed properties and logs them.
|
||||
|
||||
`ngOnChanges()` 方法获取了一个对象,它把每个发生变化的属性名都映射到了一个[SimpleChange](api/core/SimpleChange)对象,
|
||||
该对象中有属性的当前值和前一个值。我们在这些发生了变化的属性上进行迭代,并记录它们。
|
||||
该对象中有属性的当前值和前一个值。这个钩子会在这些发生了变化的属性上进行迭代,并记录它们。
|
||||
|
||||
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
|
||||
|
||||
|
@ -824,7 +824,7 @@ It writes a special message to the log when there are no substantive changes to
|
|||
so you can see how often `DoCheck` is called. The results are illuminating:
|
||||
|
||||
该代码检测一些**相关的值**,捕获当前值并与以前的值进行比较。
|
||||
当英雄或它的超能力发生了非实质性改变时,我们就往日志中写一条特殊的消息。
|
||||
当英雄或它的超能力发生了非实质性改变时,就会往日志中写一条特殊的消息。
|
||||
这样你可以看到 `DoCheck` 被调用的频率。结果非常显眼:
|
||||
|
||||
<figure>
|
||||
|
@ -836,7 +836,7 @@ This hook is called with enormous frequency—after _every_
|
|||
change detection cycle no matter where the change occurred.
|
||||
It's called over twenty times in this example before the user can do anything.
|
||||
|
||||
虽然 `ngDoCheck()` 钩子可以可以监测到英雄的 `name` 什么时候发生了变化。但我们必须小心。
|
||||
虽然 `ngDoCheck()` 钩子可以可以监测到英雄的 `name` 什么时候发生了变化。但其开销很恐怖。
|
||||
这个 `ngDoCheck` 钩子被非常频繁的调用 —— 在_每次_变更检测周期之后,发生了变化的每个地方都会调它。
|
||||
在这个例子中,用户还没有做任何操作之前,它就被调用了超过二十次。
|
||||
|
||||
|
@ -877,7 +877,7 @@ The following hooks take action based on changing values *within the child view*
|
|||
which can only be reached by querying for the child view via the property decorated with
|
||||
[@ViewChild](api/core/ViewChild).
|
||||
|
||||
下列钩子基于*子视图中*的每一次数据变更采取行动,我们只能通过带[@ViewChild](api/core/ViewChild)装饰器的属性来访问子视图。
|
||||
下列钩子基于*子视图中*的每一次数据变更采取行动,它只能通过带[@ViewChild](api/core/ViewChild)装饰器的属性来访问子视图。
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" title="AfterViewComponent (class excerpts)" linenums="false"></code-example>
|
||||
|
||||
|
@ -907,7 +907,7 @@ Angular throws an error if the hook updates the component's data-bound `comment`
|
|||
The `LoggerService.tick_then()` postpones the log update
|
||||
for one turn of the browser's JavaScript cycle and that's just long enough.
|
||||
|
||||
如果我们立即更新组件中被绑定的 `comment` 属性,Angular 就会抛出一个错误(试试!)。
|
||||
如果立即更新组件中被绑定的 `comment` 属性,Angular 就会抛出一个错误(试试!)。
|
||||
`LoggerService.tick_then()` 方法延迟更新日志一个回合(浏览器 JavaScript 周期回合),这样就够了。
|
||||
|
||||
Here's *AfterView* in action:
|
||||
|
@ -959,7 +959,7 @@ This time, instead of including the child view within the template, it imports t
|
|||
the `AfterContentComponent`'s parent. Here's the parent's template:
|
||||
|
||||
对比[前一个](guide/lifecycle-hooks#afterview)例子考虑这个变化。
|
||||
这次,我们不再通过模板来把子视图包含进来,而是改从 `AfterContentComponent` 的父组件中导入它。下面是父组件的模板:
|
||||
这次不再通过模板来把子视图包含进来,而是改为从 `AfterContentComponent` 的父组件中导入它。下面是父组件的模板:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" title="AfterContentParentComponent (template excerpt)" linenums="false"></code-example>
|
||||
|
||||
|
@ -968,7 +968,7 @@ Never put content between a component's element tags *unless you intend to proje
|
|||
into the component*.
|
||||
|
||||
注意,`<my-child>` 标签被包含在 `<after-content>` 标签中。
|
||||
永远不要在组件标签的内部放任何内容 —— *除非我们想把这些内容投影进这个组件中*。
|
||||
永远不要在组件标签的内部放任何内容 —— *除非你想把这些内容投影进这个组件中*。
|
||||
|
||||
Now look at the component's template:
|
||||
|
||||
|
@ -1029,7 +1029,7 @@ The following *AfterContent* hooks take action based on changing values in a *co
|
|||
which can only be reached by querying for them via the property decorated with
|
||||
[@ContentChild](api/core/ContentChild).
|
||||
|
||||
下列 *AfterContent* 钩子基于*子级内容*中值的变化而采取相应的行动,这里我们只能通过带有[@ContentChild](api/core/ContentChild)装饰器的属性来查询到“子级内容”。
|
||||
下列 *AfterContent* 钩子基于*子级内容*中值的变化而采取相应的行动,它只能通过带有[@ContentChild](api/core/ContentChild)装饰器的属性来查询到“子级内容”。
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" title="AfterContentComponent (class excerpts)" linenums="false"></code-example>
|
||||
|
||||
|
@ -1049,7 +1049,6 @@ Recall that Angular calls both *AfterContent* hooks before calling either of the
|
|||
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
||||
There is a small window between the `AfterContent...` and `AfterView...` hooks to modify the host view.
|
||||
|
||||
|
||||
回忆一下,Angular 在每次调用 *AfterView* 钩子之前也会同时调用 *AfterContent*。
|
||||
Angular 在完成当前组件的视图合成之前,就已经完成了被投影内容的合成。
|
||||
所以我们仍然有机会去修改那个视图。
|
||||
所以你仍然有机会去修改那个视图。
|
||||
|
|
|
@ -16,7 +16,7 @@ A basic understanding of the following concepts:
|
|||
|
||||
NgModules help organize an application into cohesive blocks of functionality.
|
||||
|
||||
NgModules 可以帮我们把应用组织成一些紧密相关的代码块。
|
||||
NgModules 可以帮你把应用组织成一些紧密相关的代码块。
|
||||
|
||||
This page answers the questions many developers ask about NgModule design and implementation.
|
||||
|
||||
|
@ -95,13 +95,13 @@ strings, numbers, functions, entity models, configurations, business logic, and
|
|||
`AppComponent` is often listed in both `declarations` and `bootstrap`.
|
||||
You might see the same component listed in `declarations`, `exports`, and `entryComponents`.
|
||||
|
||||
我们经常看到 `AppComponent` 被同时列在 `declarations` 和 `bootstrap` 中。
|
||||
我们还可能看到 `HeroComponent` 被同时列在 `declarations`、`exports` 和 `entryComponent` 中。
|
||||
`AppComponent` 经常被同时列在 `declarations` 和 `bootstrap` 中。
|
||||
另外你还可能看到 `HeroComponent` 被同时列在 `declarations`、`exports` 和 `entryComponent` 中。
|
||||
|
||||
While that seems redundant, these properties have different functions.
|
||||
Membership in one list doesn't imply membership in another list.
|
||||
|
||||
这*看起来*是多余的,不过这些函数具有不同的功能,我们无法从它出现在一个列表中推断出它也应该在另一个列表中。
|
||||
这*看起来*是多余的,不过这些函数具有不同的功能,从它出现在一个列表中无法推断出它也应该在另一个列表中。
|
||||
|
||||
* `AppComponent` could be declared in this module but not bootstrapped.
|
||||
|
||||
|
@ -113,7 +113,7 @@ Membership in one list doesn't imply membership in another list.
|
|||
|
||||
* A component could be imported from another app module (so you can't declare it) and re-exported by this module.
|
||||
|
||||
`HeroComponent` 可能是从另一个应用模块中导入的(所以我们没法声明它)并且被当前模块重新导出。
|
||||
`HeroComponent` 可能是从另一个应用模块中导入的(所以你没法声明它)并且被当前模块重新导出。
|
||||
|
||||
* A component could be exported for inclusion in an external component's template
|
||||
as well as dynamically loaded in a pop-up dialog.
|
||||
|
@ -353,7 +353,7 @@ You add that result to the `imports` list of the root `AppModule`.
|
|||
`RouterModule.forRoot()` 就是一个很好的例子。
|
||||
应用把一个 `Routes` 对象传给 `RouterModule.forRoot()`,为的就是使用路由配置全应用级的 `Router` 服务。
|
||||
`RouterModule.forRoot()` 返回一个[ModuleWithProviders](api/core/ModuleWithProviders)对象。
|
||||
我们把这个结果添加到根模块 `AppModule` 的 `imports` 列表中。
|
||||
你把这个结果添加到根模块 `AppModule` 的 `imports` 列表中。
|
||||
|
||||
Only call and import a `.forRoot()` result in the root application module, `AppModule`.
|
||||
Importing it in any other module, particularly in a lazy-loaded module,
|
||||
|
@ -397,7 +397,7 @@ When you import an NgModule,
|
|||
Angular adds the module's service providers (the contents of its `providers` list)
|
||||
to the application root injector.
|
||||
|
||||
当我们导入一个模块时,Angular 就会把该模块的服务提供商(也就是它的 `providers` 列表中的内容)加入该应用的*根注入器*中。
|
||||
当你导入一个模块时,Angular 就会把该模块的服务提供商(也就是它的 `providers` 列表中的内容)加入该应用的*根注入器*中。
|
||||
|
||||
This makes the provider visible to every class in the application that knows the provider's lookup token, or knows its name.
|
||||
|
||||
|
@ -490,7 +490,7 @@ The `AppModule` always wins.
|
|||
|
||||
## How do I restrict service scope to a module?
|
||||
|
||||
## 我们应该如何把服务的范围限制到模块中?
|
||||
## 我应该如何把服务的范围限制到模块中?
|
||||
|
||||
When a module is loaded at application launch,
|
||||
its `@NgModule.providers` have *application-wide scope*;
|
||||
|
@ -659,7 +659,7 @@ the service with the component.
|
|||
Then each new instance of the component gets its own cached service instance.
|
||||
The changes that editor makes in its service don't touch the instances elsewhere in the application.
|
||||
|
||||
例如,如果英雄编辑组件需要自己私有的缓存英雄服务实例,那么我们应该把 `HeroService` 注册进 `HeroEditorComponent` 中。
|
||||
例如,如果英雄编辑组件需要自己私有的缓存英雄服务实例,那就应该把 `HeroService` 注册进 `HeroEditorComponent` 中。
|
||||
这样,每个新的 `HeroEditorComponent` 的实例都会得到一份自己的缓存服务实例。
|
||||
编辑器的改动只会作用于它自己的服务,而不会影响到应用中其它地方的英雄实例。
|
||||
|
||||
|
@ -716,7 +716,7 @@ than the app-wide singleton version that Angular injected in one of the eagerly
|
|||
|
||||
This scenario causes your app to create a new instance every time, instead of using the singleton.
|
||||
|
||||
这个场景导致我们的应用每次都创建一个新的服务实例,而不是使用单例的服务。
|
||||
这个场景导致你的应用每次都创建一个新的服务实例,而不是使用单例的服务。
|
||||
|
||||
<!--KW--What does this cause? I wasn't able to get the suggestion of this to work from
|
||||
the current FAQ:
|
||||
|
@ -793,7 +793,7 @@ To prevent this issue, write a constructor that attempts to inject the module or
|
|||
from the root app injector. If the injection succeeds, the class has been loaded a second time.
|
||||
You can throw an error or take other remedial action.
|
||||
|
||||
为了防范这种风险,我们可以写一个构造函数,它会尝试从应用的根注入器中注入该模块或服务。如果这种注入成功了,那就说明这个类是被第二次加载的,我们就可以抛出一个错误,或者采取其它挽救措施。
|
||||
为了防范这种风险,可以写一个构造函数,它会尝试从应用的根注入器中注入该模块或服务。如果这种注入成功了,那就说明这个类是被第二次加载的,我们就可以抛出一个错误,或者采取其它挽救措施。
|
||||
|
||||
Certain NgModules, such as `BrowserModule`, implement such a guard.
|
||||
Here is a custom constructor for an NgModule called `CoreModule`.
|
||||
|
@ -922,15 +922,15 @@ For more information, see [Entry Components](guide/entry-components).
|
|||
The reason is _tree shaking_. For production apps you want to load the smallest, fastest code possible. The code should contain only the classes that you actually need.
|
||||
It should exclude a component that's never used, whether or not that component is declared.
|
||||
|
||||
原因在于*摇树优化*。对于产品化应用,我们希望加载尽可能小而快的代码。
|
||||
原因在于*摇树优化*。对于产品化应用,你会希望加载尽可能小而快的代码。
|
||||
代码中应该仅仅包括那些实际用到的类。
|
||||
它应该排除那些我们从未用过的组件,无论该组件是否被声明过。
|
||||
它应该排除那些从未用过的组件,无论该组件是否被声明过。
|
||||
|
||||
In fact, many libraries declare and export components you'll never use.
|
||||
If you don't reference them, the tree shaker drops these components from the final code package.
|
||||
|
||||
事实上,大多数库中声明和导出的组件我们都用不到。
|
||||
如果我们从未引用它们,那么*摇树优化器*就会从最终的代码包中把这些组件砍掉。
|
||||
事实上,大多数库中声明和导出的组件你都用不到。
|
||||
如果你从未引用它们,那么*摇树优化器*就会从最终的代码包中把这些组件砍掉。
|
||||
|
||||
If the [Angular compiler](guide/ngmodule-faq#q-angular-compiler) generated code for every declared component, it would defeat the purpose of the tree shaker.
|
||||
|
||||
|
@ -938,7 +938,7 @@ If the [Angular compiler](guide/ngmodule-faq#q-angular-compiler) generated code
|
|||
|
||||
Instead, the compiler adopts a recursive strategy that generates code only for the components you use.
|
||||
|
||||
所以,编译器转而采用一种递归策略,它只为我们用到的那些组件生成代码。
|
||||
所以,编译器转而采用一种递归策略,它只为你用到的那些组件生成代码。
|
||||
|
||||
The compiler starts with the entry components,
|
||||
then it generates code for the declared components it [finds](guide/ngmodule-faq#q-template-reference) in an entry component's template,
|
||||
|
@ -1047,7 +1047,7 @@ In modern JavaScript, every file is a module
|
|||
Within each file you write an `export` statement to make parts of the module public.
|
||||
|
||||
在现代 JavaScript 中,每个文件都是模块(参见[模块](http://exploringjs.com/es6/ch_modules.html))。
|
||||
在每个文件中,我们写一个 `export` 语句将模块的一部分公开。
|
||||
在每个文件中,你要写一个 `export` 语句将模块的一部分公开。
|
||||
|
||||
An Angular NgModule is a class with the `@NgModule` decorator—JavaScript modules
|
||||
don't have to have the `@NgModule` decorator. Angular's `NgModule` has `imports` and `exports` and they serve a similar purpose.
|
||||
|
@ -1058,8 +1058,8 @@ Angular 的 `NgModule` 有自己的 `imports` 和 `exports` 来达到类似的
|
|||
You _import_ other NgModules so you can use their exported classes in component templates.
|
||||
You _export_ this NgModule's classes so they can be imported and used by components of _other_ NgModules.
|
||||
|
||||
我们可以*导入*其它 Angular 模块,以便在当前模块的组件模板中使用它们导出的类。
|
||||
我们可以*导出*当前 Angular 模块中的类,以便其它模块可以导入它们,并用在自己的组件模板中。
|
||||
你可以*导入*其它 Angular 模块,以便在当前模块的组件模板中使用它们导出的类。
|
||||
你可以*导出*当前 Angular 模块中的类,以便其它模块可以导入它们,并用在自己的组件模板中。
|
||||
|
||||
For more information, see [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
|
||||
|
||||
|
@ -1102,13 +1102,13 @@ Angular 只查询两种组件、指令或管道:1)那些在当前模块中
|
|||
The Angular compiler converts the application code you write into highly performant JavaScript code.
|
||||
The `@NgModule` metadata plays an important role in guiding the compilation process.
|
||||
|
||||
*Angular 编译器*会把我们所写的应用代码转换成高性能的 JavaScript 代码。
|
||||
*Angular 编译器*会把你所编写的应用代码转换成高性能的 JavaScript 代码。
|
||||
在编译过程中,`@NgModule` 的元数据扮演了很重要的角色。
|
||||
|
||||
The code you write isn't immediately executable. For example, components have templates that contain custom elements, attribute directives, Angular binding declarations,
|
||||
and some peculiar syntax that clearly isn't native HTML.
|
||||
|
||||
我们写的代码是无法直接执行的。
|
||||
你写的代码是无法直接执行的。
|
||||
比如**组件**。
|
||||
组件有一个模板,其中包含了自定义元素、属性型指令、Angular 绑定声明和一些显然不属于原生 HTML 的古怪语法。
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ Consider using [nvm](https://github.com/creationix/nvm) for managing multiple
|
|||
versions of node and npm. You may need [nvm](https://github.com/creationix/nvm) if
|
||||
you already have projects running on your machine that use other versions of node and npm.
|
||||
|
||||
我们建议使用[nvm](https://github.com/creationix/nvm)来管理 node 和 npm 的多个版本。如果你机器上已经有某些项目运行了 node 和 npm 的其它版本,你就会需要[nvm](https://github.com/creationix/nvm)了。
|
||||
建议使用[nvm](https://github.com/creationix/nvm)来管理 node 和 npm 的多个版本。如果你机器上已经有某些项目运行了 node 和 npm 的其它版本,你就会需要[nvm](https://github.com/creationix/nvm)了。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -58,7 +58,7 @@ CLI 的 `ng new` 命令会给项目创建一个默认的 `package.json` 文件
|
|||
You will add packages to `package.json` as your application evolves.
|
||||
You may even remove some.
|
||||
|
||||
随着应用的成长,我们还会往 `package.json` 中添加更多包,甚至可能会移除一些。
|
||||
随着应用的成长,你还会往 `package.json` 中添加更多包,甚至可能会移除一些。
|
||||
|
||||
This guide focuses on the most important packages in the starter set.
|
||||
|
||||
|
@ -197,11 +197,11 @@ which polyfills missing features for several popular browser.
|
|||
|
||||
The packages listed in the *devDependencies* section of the `package.json` help you develop the application on your local machine.
|
||||
|
||||
`package.json` 的 *devDependencies* 区列出的这些包可以帮助我们在本机开发应用。
|
||||
`package.json` 的 *devDependencies* 区列出的这些包可以帮助你在本机开发应用。
|
||||
|
||||
You don't deploy them with the production application although there is no harm in doing so.
|
||||
|
||||
我们不必在生产环境的应用中部署它们,当然,就算部署了也没什么坏处。
|
||||
你不必在生产环境的应用中部署它们,当然,就算部署了也没什么坏处。
|
||||
|
||||
**[@angular/cli](https://github.com/angular/angular-cli/)**: The Angular CLI tools.
|
||||
|
||||
|
@ -268,7 +268,7 @@ You can remove packages that you don't need but how can you be sure that you won
|
|||
As a practical matter, it's better to install a package you don't need than worry about it.
|
||||
Extra packages and package files on your local development machine are harmless.
|
||||
|
||||
我们可以移除这些不需要的包,不过我们怎么知道哪些是不需要的呢?
|
||||
你可以移除这些不需要的包,不过我们怎么知道哪些是不需要的呢?
|
||||
实际上,安装不需要的包好过担心缺少某个包。
|
||||
在你本机开发环境下存在无用的包和文件并没有害处。
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ An observable can deliver multiple values of any type—literals, messages,
|
|||
|
||||
Because of these advantages, observables are used extensively within Angular, and are recommended for app development as well.
|
||||
|
||||
由于这些优点,可观察对象在 Angular 中得到广泛使用,我们也同样建议应用开发者好好使用它。
|
||||
由于这些优点,可观察对象在 Angular 中得到广泛使用,也同样建议应用开发者好好使用它。
|
||||
|
||||
## Basic usage and terms
|
||||
|
||||
|
@ -151,7 +151,7 @@ A typical observable creates a new, independent execution for each subscribed ob
|
|||
|
||||
Sometimes, instead of starting an independent execution for each subscriber, you want each subscription to get the same values—even if values have already started emitting. This might be the case with something like an observable of clicks on the document object.
|
||||
|
||||
有时候,我们不想对每一个订阅者都独立执行一次,你可能会希望每次订阅都得到同一批值 —— 即使是那些你已经发送过的。这在某些情况下有用,比如用来发送 `document` 上的点击事件的可观察对象。
|
||||
有时候,不应该对每一个订阅者都独立执行一次,你可能会希望每次订阅都得到同一批值 —— 即使是那些你已经发送过的。这在某些情况下有用,比如用来发送 `document` 上的点击事件的可观察对象。
|
||||
|
||||
*Multicasting* is the practice of broadcasting to a list of multiple subscribers in a single execution. With a multicasting observable, you don't register multiple listeners on the document, but instead re-use the first listener and send values out to each subscriber.
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ For example, in most use cases, users prefer to see a date in a simple format li
|
|||
<samp>April 15, 1988</samp> rather than the raw string format
|
||||
<samp>Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</samp>.
|
||||
|
||||
一旦取到数据,我们可以把它们原始值的 `toString` 结果直接推入视图中。
|
||||
一旦取到数据,你就可以把它们原始值的 `toString` 结果直接推入视图中。
|
||||
但这种做法很少能具备良好的用户体验。
|
||||
比如,几乎每个人都更喜欢简单的日期格式,例如<samp>1988-04-15</samp>,而不是服务端传过来的原始字符串格式 —— <samp>Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</samp>。
|
||||
|
||||
|
@ -23,12 +23,12 @@ desire many of the same transformations repeatedly, both within and across many
|
|||
You can almost think of them as styles.
|
||||
In fact, you might like to apply them in your HTML templates as you do styles.
|
||||
|
||||
显然,有些值最好显示成用户友好的格式。我们很快就会发现,在很多不同的应用中,都在重复做出某些相同的变换。
|
||||
我们几乎会把它们看做某种 CSS 样式,事实上,我们也确实更喜欢在 HTML 模板中应用它们 —— 就像 CSS 样式一样。
|
||||
显然,有些值最好显示成用户友好的格式。你很快就会发现,在很多不同的应用中,都在重复做出某些相同的变换。
|
||||
你几乎会把它们看做某种 CSS 样式,事实上,你也确实更喜欢在 HTML 模板中应用它们 —— 就像 CSS 样式一样。
|
||||
|
||||
Introducing Angular pipes, a way to write display-value transformations that you can declare in your HTML.
|
||||
|
||||
通过引入 Angular 管道,我们可以把这种简单的“显示-值”转换器声明在 HTML 中。
|
||||
通过引入 Angular 管道,你可以把这种简单的“显示-值”转换器声明在 HTML 中。
|
||||
|
||||
You can run the <live-example></live-example> in Stackblitz and download the code from there.
|
||||
|
||||
|
@ -43,7 +43,7 @@ In this page, you'll use pipes to transform a component's birthday property into
|
|||
a human-friendly date.
|
||||
|
||||
管道把数据作为输入,然后转换它,给出期望的输出。
|
||||
我们将把组件的 `birthday` 属性转换成对人类更友好的日期格式,来说明这一点:
|
||||
你要把组件的 `birthday` 属性转换成对人类更友好的日期格式。
|
||||
|
||||
<code-example path="pipes/src/app/hero-birthday1.component.ts" title="src/app/hero-birthday1.component.ts" linenums="false">
|
||||
|
||||
|
@ -61,7 +61,7 @@ Inside the interpolation expression, you flow the component's `birthday` value t
|
|||
[pipe operator](guide/template-syntax#pipe) ( | ) to the [Date pipe](api/common/DatePipe)
|
||||
function on the right. All pipes work this way.
|
||||
|
||||
在这个插值表达式中,我们让组件的 `birthday` 值通过[管道操作符](guide/template-syntax#pipe)( | )流动到
|
||||
在这个插值表达式中,你让组件的 `birthday` 值通过[管道操作符](guide/template-syntax#pipe)( | )流动到
|
||||
右侧的[Date 管道](api/common/DatePipe)函数中。所有管道都会用这种方式工作。
|
||||
|
||||
## Built-in pipes
|
||||
|
@ -97,13 +97,13 @@ To add parameters to a pipe, follow the pipe name with a colon ( : ) and then th
|
|||
(such as `currency:'EUR'`). If the pipe accepts multiple parameters, separate the values with colons (such as `slice:1:5`)
|
||||
|
||||
管道可能接受任何数量的可选参数来对它的输出进行微调。
|
||||
我们可以在管道名后面添加一个冒号( : )再跟一个参数值,来为管道添加参数(比如 `currency:'EUR'`)。
|
||||
如果我们的管道可以接受多个参数,那么就用冒号来分隔这些参数值(比如 `slice:1:5`)。
|
||||
可以在管道名后面添加一个冒号( : )再跟一个参数值,来为管道添加参数(比如 `currency:'EUR'`)。
|
||||
如果这个管道可以接受多个参数,那么就用冒号来分隔这些参数值(比如 `slice:1:5`)。
|
||||
|
||||
Modify the birthday template to give the date pipe a format parameter.
|
||||
After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**:
|
||||
|
||||
我们将通过修改生日模板来给这个日期管道提供一个格式化参数。
|
||||
修改生日模板,来为这个日期管道提供一个格式化参数。
|
||||
当格式化完该英雄的 4 月 15 日生日之后,它应该被渲染成**<samp>04/15/88</samp>**。
|
||||
|
||||
<code-example path="pipes/src/app/app.component.html" region="format-birthday" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -117,12 +117,12 @@ such as a string literal or a component property.
|
|||
In other words, you can control the format through a binding the same way you control the birthday value through a binding.
|
||||
|
||||
参数值可以是任何有效的模板表达式(参见[模板语法](guide/template-syntax)中的[模板表达式](guide/template-syntax#template-expressions)部分),比如字符串字面量或组件的属性。
|
||||
换句话说,借助属性绑定,我们也可以像用绑定来控制生日的值一样,控制生日的显示格式。
|
||||
换句话说,借助属性绑定,你也可以像用绑定来控制生日的值一样,控制生日的显示格式。
|
||||
|
||||
Write a second component that *binds* the pipe's format parameter
|
||||
to the component's `format` property. Here's the template for that component:
|
||||
|
||||
我们来写第二个组件,它把管道的格式参数*绑定*到该组件的 `format` 属性。这里是新组件的模板:
|
||||
来写第二个组件,它把管道的格式参数*绑定*到该组件的 `format` 属性。这里是新组件的模板:
|
||||
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" title="src/app/hero-birthday2.component.ts (template)" linenums="false">
|
||||
|
||||
|
@ -132,7 +132,7 @@ You also added a button to the template and bound its click event to the compone
|
|||
That method toggles the component's `format` property between a short form
|
||||
(`'shortDate'`) and a longer form (`'fullDate'`).
|
||||
|
||||
我们还能在模板中添加一个按钮,并把它的点击事件绑定到组件的 `toggleFormat()` 方法。
|
||||
你还能在模板中添加一个按钮,并把它的点击事件绑定到组件的 `toggleFormat()` 方法。
|
||||
此方法会在短日期格式(`'shortDate'`)和长日期格式(`'fullDate'`)之间切换组件的 `format` 属性。
|
||||
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" title="src/app/hero-birthday2.component.ts (class)" linenums="false">
|
||||
|
@ -143,7 +143,7 @@ As you click the button, the displayed date alternates between
|
|||
"**<samp>04/15/1988</samp>**" and
|
||||
"**<samp>Friday, April 15, 1988</samp>**".
|
||||
|
||||
当我们点击按钮的时候,显示的日志会在“**<samp>04/15/1988</samp>**”和“**<samp>Friday, April 15, 1988</samp>**”之间切换。
|
||||
当你点击按钮的时候,显示的日志会在“**<samp>04/15/1988</samp>**”和“**<samp>Friday, April 15, 1988</samp>**”之间切换。
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle">
|
||||
|
@ -167,9 +167,9 @@ In the following example, to display the birthday in uppercase,
|
|||
the birthday is chained to the `DatePipe` and on to the `UpperCasePipe`.
|
||||
The birthday displays as **<samp>APR 15, 1988</samp>**.
|
||||
|
||||
我们可以把管道链在一起,以组合出一些潜在的有用功能。
|
||||
下面这个例子中,我们把 `birthday` 链到 `DatePipe` 管道,然后又链到 `UpperCasePipe`,这样我们就可以把生日显示成大写形式了。
|
||||
比如下面的代码就会把生日显示成**<samp>APR 15, 1988</samp>**:
|
||||
你可以把管道串联在一起,以组合出一些潜在的有用功能。
|
||||
下面这个例子中,要把 `birthday` 串联到 `DatePipe` 管道,然后又串联到 `UpperCasePipe`,这样就可以把生日显示成大写形式了。
|
||||
生日被显示成了**<samp>APR 15, 1988</samp>**:
|
||||
|
||||
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" title="src/app/app.component.html" linenums="false">
|
||||
|
||||
|
@ -191,7 +191,7 @@ the same pipes as above, but passes in a parameter to `date` as well.
|
|||
You can write your own custom pipes.
|
||||
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
|
||||
|
||||
我们还可以写自己的自定义管道。
|
||||
你还可以写自己的自定义管道。
|
||||
下面就是一个名叫 `ExponentialStrengthPipe` 的管道,它可以放大英雄的能力:
|
||||
|
||||
<code-example path="pipes/src/app/exponential-strength.pipe.ts" title="src/app/exponential-strength.pipe.ts" linenums="false">
|
||||
|
@ -214,19 +214,19 @@ accepts an input value followed by optional parameters and returns the transform
|
|||
* There will be one additional argument to the `transform` method for each parameter passed to the pipe.
|
||||
Your pipe has one such parameter: the `exponent`.
|
||||
|
||||
当每个输入值被传给 `transform` 方法时,还会带上另一个参数,比如我们这个管道中的 `exponent`(放大指数)。
|
||||
当每个输入值被传给 `transform` 方法时,还会带上另一个参数,比如你这个管道就有一个 `exponent`(放大指数) 参数。
|
||||
|
||||
* To tell Angular that this is a pipe, you apply the
|
||||
`@Pipe` decorator, which you import from the core Angular library.
|
||||
|
||||
我们通过 `@Pipe` 装饰器告诉 Angular:这是一个管道。该装饰器是从 Angular 的 `core` 库中引入的。
|
||||
可以通过 `@Pipe` 装饰器来告诉 Angular:这是一个管道。该装饰器是从 Angular 的 `core` 库中引入的。
|
||||
|
||||
* The `@Pipe` decorator allows you to define the
|
||||
pipe name that you'll use within template expressions. It must be a valid JavaScript identifier.
|
||||
Your pipe's name is `exponentialStrength`.
|
||||
|
||||
这个 `@Pipe` 装饰器允许我们定义管道的名字,这个名字会被用在模板表达式中。它必须是一个有效的 JavaScript 标识符。
|
||||
比如,我们这个管道的名字是 `exponentialStrength`。
|
||||
这个 `@Pipe` 装饰器允许你定义管道的名字,这个名字会被用在模板表达式中。它必须是一个有效的 JavaScript 标识符。
|
||||
比如,你这个管道的名字是 `exponentialStrength`。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -246,7 +246,7 @@ Technically, it's optional; Angular looks for and executes the `transform` metho
|
|||
|
||||
Now you need a component to demonstrate the pipe.
|
||||
|
||||
现在,我们需要一个组件来演示这个管道。
|
||||
现在,你需要一个组件来演示这个管道。
|
||||
|
||||
<code-example path="pipes/src/app/power-booster.component.ts" title="src/app/power-booster.component.ts" linenums="false">
|
||||
|
||||
|
@ -262,11 +262,11 @@ Note the following:
|
|||
|
||||
* You use your custom pipe the same way you use built-in pipes.
|
||||
|
||||
我们使用自定义管道的方式和内置管道完全相同。
|
||||
你使用自定义管道的方式和内置管道完全相同。
|
||||
|
||||
* You must include your pipe in the `declarations` array of the `AppModule`.
|
||||
|
||||
我们必须在 `AppModule` 的 `declarations` 数组中包含这个管道。
|
||||
你必须在 `AppModule` 的 `declarations` 数组中包含这个管道。
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
|
@ -278,15 +278,15 @@ You must register custom pipes.
|
|||
If you don't, Angular reports an error.
|
||||
Angular CLI's generator registers the pipe automatically.
|
||||
|
||||
我们必须手动注册自定义管道。如果忘了,Angular 就会报告一个错误。
|
||||
在前一个例子中我们没有把 `DatePipe` 列进去,这是因为 Angular 所有的内置管道都已经预注册过了。
|
||||
你必须手动注册自定义管道。如果忘了,Angular 就会报告一个错误。
|
||||
在前一个例子中你没有把 `DatePipe` 列进去,这是因为 Angular 所有的内置管道都已经预注册过了。
|
||||
|
||||
</div>
|
||||
|
||||
To probe the behavior in the <live-example></live-example>,
|
||||
change the value and optional exponent in the template.
|
||||
|
||||
如果我们试一下这个<live-example></live-example>,就可以通过修改值和模板中的可选部分来体会其行为。
|
||||
试一下这个<live-example></live-example>,并通过修改值和模板中的可选部分来体会其行为。
|
||||
|
||||
## Power Boost Calculator
|
||||
|
||||
|
@ -297,7 +297,7 @@ Upgrade the example to a "Power Boost Calculator" that combines
|
|||
your pipe and two-way data binding with `ngModel`.
|
||||
|
||||
仅仅升级模板来测试这个自定义管道其实没多大意思。
|
||||
我们干脆把这个例子升级为“能力倍增计算器”,它可以把该管道和使用 `ngModel` 的双向数据绑定组合起来。
|
||||
干脆把这个例子升级为“能力提升计算器”,它可以把该管道和使用 `ngModel` 的双向数据绑定组合起来。
|
||||
|
||||
<code-example path="pipes/src/app/power-boost-calculator.component.ts" title="src/app/power-boost-calculator.component.ts">
|
||||
|
||||
|
@ -331,7 +331,7 @@ Angular picks a simpler, faster change detection algorithm when you use a pipe.
|
|||
In the next example, the component uses the default, aggressive change detection strategy to monitor and update
|
||||
its display of every hero in the `heroes` array. Here's the template:
|
||||
|
||||
我们下一个例子中的组件使用默认的、激进(昂贵)的变更检测策略来检测和更新 `heroes` 数组中的每个英雄。下面是它的模板:
|
||||
在下一个例子中,组件使用默认的、激进(昂贵)的变更检测策略来检测和更新 `heroes` 数组中的每个英雄。下面是它的模板:
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" title="src/app/flying-heroes.component.html (v1)" linenums="false">
|
||||
|
||||
|
@ -349,9 +349,9 @@ You can add heroes and Angular updates the display when you do.
|
|||
If you click the `reset` button, Angular replaces `heroes` with a new array of the original heroes and updates the display.
|
||||
If you added the ability to remove or change a hero, Angular would detect those changes and update the display as well.
|
||||
|
||||
我们可以添加新的英雄,加完之后,Angular 就会更新显示。
|
||||
你可以添加新的英雄,加完之后,Angular 就会更新显示。
|
||||
`reset` 按钮会把 `heroes` 替换成一个由原来的英雄组成的新数组,重置完之后,Angular 就会更新显示。
|
||||
如果我们提供了删除或修改英雄的能力,Angular 也会检测到那些更改,并更新显示。
|
||||
如果你提供了删除或修改英雄的能力,Angular 也会检测到那些更改,并更新显示。
|
||||
|
||||
<h3 class="no-toc"><i>FlyingHeroesPipe</i></h3>
|
||||
|
||||
|
@ -359,7 +359,7 @@ If you added the ability to remove or change a hero, Angular would detect those
|
|||
|
||||
Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
|
||||
|
||||
我们来往 `*ngFor` 重复器中添加一个 `FlyingHeroesPipe` 管道,这个管道能过滤出所有会飞的英雄。
|
||||
往 `*ngFor` 重复器中添加一个 `FlyingHeroesPipe` 管道,这个管道能过滤出所有会飞的英雄。
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" title="src/app/flying-heroes.component.html (flyers)" linenums="false">
|
||||
|
||||
|
@ -367,7 +367,7 @@ Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroe
|
|||
|
||||
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
|
||||
|
||||
下面是 `FlyingHeroesPipe` 的实现,它遵循了我们以前见过的那些写自定义管道的模式。
|
||||
下面是 `FlyingHeroesPipe` 的实现,它遵循了以前讲过的那些写自定义管道的模式。
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" title="src/app/flying-heroes.pipe.ts" linenums="false">
|
||||
|
||||
|
@ -376,17 +376,17 @@ Here's the `FlyingHeroesPipe` implementation, which follows the pattern for cust
|
|||
Notice the odd behavior in the <live-example></live-example>:
|
||||
when you add flying heroes, none of them are displayed under "Heroes who fly."
|
||||
|
||||
当运行<live-example></live-example>时,我们看到一种奇怪的行为。添加的每个英雄都是会飞行的英雄,但是没有一个被显示出来。
|
||||
当运行<live-example></live-example>时,你看到一种奇怪的行为。添加的每个英雄都是会飞行的英雄,但是没有一个被显示出来。
|
||||
|
||||
Although you're not getting the behavior you want, Angular isn't broken.
|
||||
It's just using a different change-detection algorithm that ignores changes to the list or any of its items.
|
||||
|
||||
虽然我们没有得到期望的行为,但 Angular 也没有出错。
|
||||
虽然你没有得到期望的行为,但 Angular 也没有出错。
|
||||
这里只是用了另一种变更检测算法 —— 它会忽略对列表及其子项所做的任何更改。
|
||||
|
||||
Notice how a hero is added:
|
||||
|
||||
来看看我们是如何添加新英雄的:
|
||||
注意这里是如何添加新英雄的:
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" title="src/app/flying-heroes.component.ts" linenums="false">
|
||||
|
||||
|
@ -395,14 +395,14 @@ Notice how a hero is added:
|
|||
You add the hero into the `heroes` array. The reference to the array hasn't changed.
|
||||
It's the same array. That's all Angular cares about. From its perspective, *same array, no change, no display update*.
|
||||
|
||||
当我们往 `heroes` 数组中添加一个新的英雄时,这个数组的引用并没有改变。它还是那个数组。而引用却是 Angular 所关心的一切。
|
||||
当你往 `heroes` 数组中添加一个新的英雄时,这个数组的引用并没有改变。它还是那个数组。而引用却是 Angular 所关心的一切。
|
||||
从 Angular 的角度来看,*这是同一个数组,没有变化,也就不需要更新显示*。
|
||||
|
||||
To fix that, create an array with the new hero appended and assign that to `heroes`.
|
||||
This time Angular detects that the array reference has changed.
|
||||
It executes the pipe and updates the display with the new array, which includes the new flying hero.
|
||||
|
||||
我们可以修复它。让我们创建一个新数组,把这个英雄追加进去,并把它赋给 `heroes`。
|
||||
要修复它,就要创建一个新数组,把这个英雄追加进去,并把它赋给 `heroes`。
|
||||
这次,Angular 检测到数组的引用变化了。它执行了这个管道,并使用这个新数组更新显示,这次它就包括新的飞行英雄了。
|
||||
|
||||
If you *mutate* the array, no pipe is invoked and the display isn't updated;
|
||||
|
@ -410,9 +410,9 @@ if you *replace* the array, the pipe executes and the display is updated.
|
|||
The Flying Heroes application extends the
|
||||
code with checkbox switches and additional displays to help you experience these effects.
|
||||
|
||||
如果我们**修改了**这个数组,没有管道被执行,也没有显示被更新。
|
||||
如果我们**替换了**这个数组,管道就会被执行,显示也更新了。
|
||||
这个*飞行英雄*的例子用检查框和其它显示内容扩展了原有代码,来帮我们体验这些效果。
|
||||
如果你**修改了**这个数组,没有管道被执行,也没有显示被更新。
|
||||
如果你**替换了**这个数组,管道就会被执行,显示也更新了。
|
||||
这个*飞行英雄*的例子用检查框和其它显示内容扩展了原有代码,来帮你体验这些效果。
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes">
|
||||
|
@ -424,7 +424,7 @@ That's an easy rule to follow in *this* example
|
|||
where the only way to change the data is by adding a hero.
|
||||
|
||||
直接替换这个数组是通知 Angular 更新显示的一种高效方式。
|
||||
我们该什么时候替换这个数组呢?当数据变化的时候。
|
||||
你该什么时候替换这个数组呢?当数据变化的时候。
|
||||
在这个*玩具级*例子中,这是一个简单的规则,因为这里修改数据的唯一途径就是添加新英雄。
|
||||
|
||||
More often, you don't know when the data have changed,
|
||||
|
@ -435,13 +435,13 @@ Moreover, it's unwise to distort the component design to accommodate a pipe.
|
|||
Strive to keep the component class independent of the HTML.
|
||||
The component should be unaware of pipes.
|
||||
|
||||
更多情况下,我们不知道什么时候数据变化了,尤其是在那些有很多种途径改动数据的程序中 —— 可能在程序中很远的地方。
|
||||
组件就是一个通常无法知道那些改动的例子。此外,它会导致削足适履 —— 扭曲我们的组件设计来适应管道。
|
||||
我们要尽可能保持组件类独立于 HTML。组件不应该关心管道的存在。
|
||||
更多情况下,你不知道什么时候数据变化了,尤其是在那些有很多种途径改动数据的程序中 —— 可能在程序中很远的地方。
|
||||
组件就是一个通常无法知道那些改动的例子。此外,它会导致削足适履 —— 扭曲组件的设计来适应管道。
|
||||
要尽可能保持组件类独立于 HTML。组件不应该关心管道的存在。
|
||||
|
||||
For filtering flying heroes, consider an *impure pipe*.
|
||||
|
||||
为了过滤会飞的英雄,我们要使用*非纯(impure)管道*。
|
||||
为了过滤出会飞的英雄,考虑使用*非纯(impure)管道*。
|
||||
|
||||
## Pure and impure pipes
|
||||
|
||||
|
@ -453,8 +453,8 @@ You make a pipe impure by setting its pure flag to false. You could make the `Fl
|
|||
impure like this:
|
||||
|
||||
有两类管道:**纯**的与**非纯**的。
|
||||
默认情况下,管道都是纯的。我们以前见到的每个管道都是纯的。
|
||||
通过把它的 `pure` 标志设置为 `false`,我们可以制作一个非纯管道。我们可以像这样让 `FlyingHeroesPipe` 变成非纯的:
|
||||
默认情况下,管道都是纯的。以前见到的每个管道都是纯的。
|
||||
通过把它的 `pure` 标志设置为 `false`,你可以制作一个非纯管道。你可以像这样让 `FlyingHeroesPipe` 变成非纯的:
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" title="src/app/flying-heroes.pipe.ts" linenums="false">
|
||||
|
||||
|
@ -462,7 +462,7 @@ impure like this:
|
|||
|
||||
Before doing that, understand the difference between pure and impure, starting with a pure pipe.
|
||||
|
||||
在继续往下走之前,我们先理解一下*纯*和*非纯*之间的区别,从*纯*管道开始。
|
||||
在继续往下走之前,先理解一下*纯*和*非纯*之间的区别,从*纯*管道开始。
|
||||
|
||||
<h3 class="no-toc">Pure pipes</h3>
|
||||
|
||||
|
@ -480,7 +480,7 @@ Angular ignores changes within (composite) objects.
|
|||
It won't call a pure pipe if you change an input month, add to an input array, or update an input object property.
|
||||
|
||||
Angular 会忽略(复合)对象*内部*的更改。
|
||||
如果我们更改了输入日期(`Date`)中的月份、往一个输入数组(`Array`)中添加新值或者更新了一个输入对象(`Object`)的属性,Angular 都不会调用纯管道。
|
||||
如果你更改了输入日期(`Date`)中的月份、往一个输入数组(`Array`)中添加新值或者更新了一个输入对象(`Object`)的属性,Angular 都不会调用纯管道。
|
||||
|
||||
This may seem restrictive but it's also fast.
|
||||
An object reference check is fast—much faster than a deep check for
|
||||
|
@ -493,8 +493,8 @@ pipe execution and a view update.
|
|||
For this reason, a pure pipe is preferable when you can live with the change detection strategy.
|
||||
When you can't, you *can* use the impure pipe.
|
||||
|
||||
因此,如果我们要和变更检测策略打交道,就会更喜欢用纯管道。
|
||||
如果不能,我们就*可以*转回到非纯管道。
|
||||
因此,如果要和变更检测策略打交道,就会更喜欢用纯管道。
|
||||
如果不能,你就*可以*转回到非纯管道。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -502,8 +502,8 @@ Or you might not use a pipe at all.
|
|||
It may be better to pursue the pipe's purpose with a property of the component,
|
||||
a point that's discussed later in this page.
|
||||
|
||||
或者我们也可以完全不用管道。
|
||||
有时候,使用组件的属性能比用管道更好的达到目的,这一点我们等后面会再提起。
|
||||
或者你也可以完全不用管道。
|
||||
有时候,使用组件的属性能比用管道更好的达到目的,后面会再讨论这一点。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -520,7 +520,7 @@ Angular 会在每个组件的变更检测周期中执行*非纯管道*。
|
|||
With that concern in mind, implement an impure pipe with great care.
|
||||
An expensive, long-running pipe could destroy the user experience.
|
||||
|
||||
要在脑子里绷着这根弦,我们必须小心翼翼的实现非纯管道。
|
||||
要在脑子里绷着这根弦,必须小心翼翼的实现非纯管道。
|
||||
一个昂贵、迟钝的管道将摧毁用户体验。
|
||||
|
||||
{@a impure-flying-heroes}
|
||||
|
@ -532,7 +532,7 @@ An expensive, long-running pipe could destroy the user experience.
|
|||
A flip of the switch turns the `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`.
|
||||
The complete implementation is as follows:
|
||||
|
||||
我们把 `FlyingHeroesPipe` 换成了 `FlyingHeroesImpurePipe`。
|
||||
把 `FlyingHeroesPipe` 换成了 `FlyingHeroesImpurePipe`。
|
||||
下面是完整的实现:
|
||||
|
||||
<code-tabs>
|
||||
|
@ -550,7 +550,7 @@ The complete implementation is as follows:
|
|||
You inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
|
||||
The only difference is the `pure` flag in the pipe metadata.
|
||||
|
||||
我们把它从 `FlyingHeroesPipe` 中继承下来,以证明无需改动内部代码。
|
||||
你把它从 `FlyingHeroesPipe` 中继承下来,以证明无需改动内部代码。
|
||||
唯一的区别是管道元数据中的 `pure` 标志。
|
||||
|
||||
This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
|
||||
|
@ -563,7 +563,7 @@ This is a good candidate for an impure pipe because the `transform` function is
|
|||
|
||||
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
|
||||
|
||||
我们可以从 `FlyingHeroesComponent` 派生出一个 `FlyingHeroesImpureComponent`。
|
||||
你可以从 `FlyingHeroesComponent` 派生出一个 `FlyingHeroesImpureComponent`。
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes-impure.component.html" linenums="false" title="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes">
|
||||
|
||||
|
@ -574,7 +574,7 @@ You can confirm in the <live-example></live-example> that the _flying heroes_
|
|||
display updates as you add heroes, even when you mutate the `heroes` array.
|
||||
|
||||
唯一的重大改动就是管道。
|
||||
我们可以在<live-example></live-example>中确认,当我们输入新的英雄甚至修改#[code heroes]数组时,这个#[i 会飞的英雄]的显示也跟着更新了。
|
||||
你可以在<live-example></live-example>中确认,当你添加新的英雄甚至修改 `heroes` 数组时,这个*会飞的英雄*的显示也跟着更新了。
|
||||
|
||||
{@a async-pipe}
|
||||
|
||||
|
@ -599,7 +599,7 @@ keeps delivering values from that `Observable` as they arrive.
|
|||
This next example binds an `Observable` of message strings
|
||||
(`message$`) to a view with the `async` pipe.
|
||||
|
||||
在下面例子中,我们使用该 `async` 管道把一个消息字符串(`message$`)的 `Observable` 绑定到视图中。
|
||||
下面例子使用该 `async` 管道把一个消息字符串(`message$`)的 `Observable` 绑定到视图中。
|
||||
|
||||
<code-example path="pipes/src/app/hero-async-message.component.ts" title="src/app/hero-async-message.component.ts">
|
||||
|
||||
|
@ -620,19 +620,18 @@ and have to unsubscribe when it's destroyed
|
|||
|
||||
Write one more impure pipe, a pipe that makes an HTTP request.
|
||||
|
||||
我们来写更多的非纯管道:一个向服务器发起 HTTP 请求的管道。
|
||||
来写更多的非纯管道:一个向服务器发起 HTTP 请求的管道。
|
||||
|
||||
Remember that impure pipes are called every few milliseconds.
|
||||
If you're not careful, this pipe will punish the server with requests.
|
||||
|
||||
时刻记住,非纯管道可能每隔几微秒就会被调用一次。
|
||||
如果我们不小心点,这个管道就会发起一大堆请求“攻击”服务器。
|
||||
如果你不小心点,这个管道就会发起一大堆请求“攻击”服务器。
|
||||
|
||||
In the following code, the pipe only calls the server when the request URL changes and it caches the server response.
|
||||
The code uses the [Angular http](guide/http) client to retrieve data:
|
||||
|
||||
我们确实得小心点。
|
||||
这个管道只有当所请求的 URL 发生变化时才会向服务器发起请求。它会缓存服务器的响应。
|
||||
下面这个管道只有当所请求的 URL 发生变化时才会向服务器发起请求。它会缓存服务器的响应。
|
||||
代码如下,它使用[Angular http](guide/http)客户端来接收数据
|
||||
|
||||
<code-example path="pipes/src/app/fetch-json.pipe.ts" title="src/app/fetch-json.pipe.ts">
|
||||
|
@ -642,7 +641,7 @@ The code uses the [Angular http](guide/http) client to retrieve data:
|
|||
Now demonstrate it in a harness component whose template defines two bindings to this pipe,
|
||||
both requesting the heroes from the `heroes.json` file.
|
||||
|
||||
接下来我们用一个测试台组件演示一下它,该组件的模板中定义了两个使用到此管道的绑定,他们都从 `heroes.json` 文件中取得英雄数据。
|
||||
接下来在一个测试挽具组件中演示一下它,该组件的模板中定义了两个使用到此管道的绑定,它们都从 `heroes.json` 文件中取得英雄数据。
|
||||
|
||||
<code-example path="pipes/src/app/hero-list.component.ts" title="src/app/hero-list.component.ts">
|
||||
|
||||
|
@ -678,7 +677,7 @@ In the previous code sample, the second `fetch` pipe binding demonstrates more p
|
|||
It displays the same hero data in JSON format by chaining through to the built-in `JsonPipe`.
|
||||
|
||||
第二个绑定除了用到 `FetchPipe` 之外还链接了更多管道。
|
||||
我们把获取数据的结果同时显示在第一个绑定和第二个绑定中。第二个绑定中,我们通过链接到一个内置管道 `JsonPipe` 把它转成了 JSON 格式。
|
||||
它把获取数据的结果同时显示在第一个绑定和第二个绑定中。第二个绑定中,我们通过链接到一个内置管道 `JsonPipe` 把它转成了 JSON 格式。
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
|
@ -713,11 +712,11 @@ The built-in `DatePipe` is a pure pipe with a pure function implementation.
|
|||
So are the `ExponentialStrengthPipe` and `FlyingHeroesPipe`.
|
||||
A few steps back, you reviewed the `FlyingHeroesImpurePipe`—an impure pipe with a pure function.
|
||||
|
||||
我们在本章前面见过的管道都是用纯函数实现的。
|
||||
在本章前面讨论的管道都是用纯函数实现的。
|
||||
内置的 `DatePipe` 就是一个用纯函数实现的纯管道。
|
||||
`ExponentialStrengthPipe` 是如此,
|
||||
`FlyingHeroesComponent` 也是如此。
|
||||
不久前我们刚看过的 `FlyingHeroesImpurePipe`,是一个*用纯函数实现的非纯管道*。
|
||||
不久前你刚看过的 `FlyingHeroesImpurePipe` 就是一个*用纯函数实现的非纯管道*。
|
||||
|
||||
But always implement a *pure pipe* with a *pure function*.
|
||||
Otherwise, you'll see many console errors regarding expressions that changed after they were checked.
|
||||
|
@ -733,7 +732,7 @@ transformations. Use them like styles, dropping them
|
|||
into your template's expressions to enrich the appeal and usability
|
||||
of your views.
|
||||
|
||||
管道能很好的封装和共享的通用“值-显示”转换逻辑。我们可以像样式一样使用它们,把它们扔到模板表达式中,以提升视图的表现力和可用性。
|
||||
管道能很好的封装和共享的通用“值-显示”转换逻辑。可以像样式一样使用它们,把它们扔到模板表达式中,以提升视图的表现力和可用性。
|
||||
|
||||
Explore Angular's inventory of built-in pipes in the [API Reference](api?type=pipe).
|
||||
Try writing a custom pipe and perhaps contributing it to the community.
|
||||
|
@ -762,7 +761,7 @@ Angular calls impure pipes in almost every change-detection cycle.
|
|||
|
||||
这并不是疏忽。Angular 不想提供这些管道,因为 (a) 它们性能堪忧,以及 (b) 它们会阻止比较激进的代码最小化(minification)。
|
||||
无论是 `filter` 还是 `orderBy` 都需要它的参数引用对象型属性。
|
||||
我们前面学过,这样的管道必然是[*非纯管道*](guide/pipes#pure-and-impure-pipes),并且 Angular 会在几乎每一次变更检测周期中调用非纯管道。
|
||||
你在前面学过,这样的管道必然是[*非纯管道*](guide/pipes#pure-and-impure-pipes),并且 Angular 会在几乎每一次变更检测周期中调用非纯管道。
|
||||
|
||||
Filtering and especially sorting are expensive operations.
|
||||
The user experience can degrade severely for even moderate-sized lists when Angular calls these pipe methods many times per second.
|
||||
|
@ -778,7 +777,7 @@ by offering `filter` and `orderBy` in the first place.
|
|||
The minification hazard is also compelling, if less obvious. Imagine a sorting pipe applied to a list of heroes.
|
||||
The list might be sorted by hero `name` and `planet` of origin properties in the following way:
|
||||
|
||||
虽然不是很明显,但代码最小化方面也存在风险。想象一个用于英雄列表的排序管道。我们可能根据英雄原始属性中的 `name` 和 `planet` 进行排序,就像这样:
|
||||
虽然不是很明显,但代码最小化方面也存在风险。想象一个用于英雄列表的排序管道。该列表可能根据英雄原始属性中的 `name` 和 `planet` 进行排序,就像这样:
|
||||
|
||||
<code-example language="html">
|
||||
|
||||
|
@ -792,7 +791,7 @@ You identify the sort fields by text strings, expecting the pipe to reference a
|
|||
Unfortunately, aggressive minification manipulates the `Hero` property names so that `Hero.name` and `Hero.planet`
|
||||
become something like `Hero.a` and `Hero.b`. Clearly `hero['name']` doesn't work.
|
||||
|
||||
我们使用文本字符串来标记出排序字段,期望管道通过索引形式(如 `hero['name']`)引用属性的值。
|
||||
你使用文本字符串来标记出排序字段,期望管道通过索引形式(如 `hero['name']`)引用属性的值。
|
||||
不幸的是,激进的代码最小化策略会*改变*`Hero` 类的属性名,所以 `Hero.name` 和 `Hero.planet` 可能会被变成 `Hero.a` 和 `Hero.b`。
|
||||
显然,`hero['name']` 是无法正常工作的。
|
||||
|
||||
|
@ -800,7 +799,7 @@ While some may not care to minify this aggressively,
|
|||
the Angular product shouldn't prevent anyone from minifying aggressively.
|
||||
Therefore, the Angular team decided that everything Angular provides will minify safely.
|
||||
|
||||
我们中的一些人可能不想做那么激进的最小化。但那不过是*我们的*选择而已。
|
||||
然而有些人可能不想做那么激进的最小化,
|
||||
Angular 作为一个产品不应该拒绝那些想做激进的最小化的人。
|
||||
所以,Angular 开发组决定随 Angular 一起发布的每样东西,都应该能被安全的最小化。
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ application in TypeScript, using the Angular CLI
|
|||
while adhering to the [Style Guide](guide/styleguide) recommendations that
|
||||
benefit _every_ Angular project.
|
||||
|
||||
在这一章 CLI 快速起步中,我们的目标是构建并运行一个超级简单的 Angular 应用。我们会使用 Angular-CLI 来让每个 Angular 应用从[风格指南](guide/styleguide)中获益。
|
||||
本章的目标是构建并运行一个超级简单的 TypeScript Angular 应用。使用 Angular CLI 来让*每个* Angular 应用从[风格指南](guide/styleguide)的那些建议中获益。
|
||||
|
||||
By the end of the chapter, you'll have a basic understanding of development with the CLI
|
||||
and a foundation for both these documentation samples and for real world applications.
|
||||
|
||||
在本章的末尾,我们会通过 CLI 对开发过程有一个最基本的理解,并将其作为其它文档范例以及真实应用的基础。
|
||||
在本章的末尾,你会通过 CLI 对开发过程有一个最基本的理解,并将其作为其它文档范例以及真实应用的基础。
|
||||
|
||||
And you can also <a href="generated/zips/cli-quickstart/cli-quickstart.zip" target="_blank">download the example.</a>
|
||||
|
||||
|
@ -35,7 +35,7 @@ And you can also <a href="generated/zips/cli-quickstart/cli-quickstart.zip" targ
|
|||
|
||||
You need to set up your development environment before you can do anything.
|
||||
|
||||
在开始工作之前,我们必须设置好开发环境。
|
||||
在开始工作之前,你必须设置好开发环境。
|
||||
|
||||
Install **[Node.js® and npm](https://nodejs.org/en/download/)**
|
||||
if they are not already on your machine.
|
||||
|
@ -127,13 +127,13 @@ Your app greets you with a message:
|
|||
|
||||
<h2 id='first-component'>Step 4: Edit your first Angular component</h2>
|
||||
|
||||
<h2 id='first-component'>步骤 4. 编辑我们的第一个 Angular 组件</h2>
|
||||
<h2 id='first-component'>步骤 4. 编辑你的第一个 Angular 组件</h2>
|
||||
|
||||
The CLI created the first Angular component for you.
|
||||
This is the _root component_ and it is named `app-root`.
|
||||
You can find it in `./src/app/app.component.ts`.
|
||||
|
||||
这个 CLI 为我们创建了第一个 Angular 组件。
|
||||
这个 CLI 为你创建了第一个 Angular 组件。
|
||||
它就是名叫 `app-root` 的*根组件*。
|
||||
你可以在 `./src/app/app.component.ts` 目录下找到它。
|
||||
|
||||
|
@ -145,7 +145,7 @@ Open the component file and change the `title` property from _Welcome to app!!_
|
|||
|
||||
The browser reloads automatically with the revised title. That's nice, but it could look better.
|
||||
|
||||
浏览器会自动刷新,而我们会看到修改之后的标题。不错,不过它还可以更好看一点。
|
||||
浏览器会自动刷新,并具有修改之后的标题。不错,不过它还可以更好看一点。
|
||||
|
||||
Open `src/app/app.component.css` and give the component some style.
|
||||
|
||||
|
@ -167,7 +167,7 @@ Looking good!
|
|||
|
||||
That's about all you'd expect to do in a "Hello, World" app.
|
||||
|
||||
如你所愿,我们完成了这个“Hello, World”应用。
|
||||
这就是你期待这个 “Hello, World” 应用要做的。
|
||||
|
||||
You're ready to take the [Tour of Heroes Tutorial](tutorial) and build
|
||||
a small application that demonstrates the great things you can build with Angular.
|
||||
|
@ -199,7 +199,7 @@ Whenever you want to know more about how Angular CLI works make sure to visit
|
|||
|
||||
Some of the generated files might be unfamiliar to you.
|
||||
|
||||
有些生成的文件你可能觉得陌生。接下来我们就讲讲它们。
|
||||
有些生成的文件你可能觉得陌生。
|
||||
|
||||
### The `src` folder
|
||||
|
||||
|
@ -476,7 +476,7 @@ Any files outside of this folder are meant to support building your app.
|
|||
the [Browser Support guide](guide/browser-support) for more information.
|
||||
|
||||
不同的浏览器对 Web 标准的支持程度也不同。
|
||||
腻子脚本(polyfill)能帮我们把这些不同点进行标准化。
|
||||
腻子脚本(polyfill)能把这些不同点进行标准化。
|
||||
你只要使用 `core-js` 和 `zone.js` 通常就够了,不过你也可以查看[浏览器支持指南](guide/browser-support)以了解更多信息。
|
||||
|
||||
</td>
|
||||
|
@ -689,7 +689,7 @@ These files go in the root folder next to `src/`.
|
|||
Check out the official documentation if you want to know more.
|
||||
|
||||
Angular CLI 的配置文件。
|
||||
在这个文件中,我们可以设置一系列默认值,还可以配置项目编译时要包含的那些文件。
|
||||
在这个文件中,你可以设置一系列默认值,还可以配置项目编译时要包含的那些文件。
|
||||
要了解更多,请参阅它的官方文档。
|
||||
|
||||
</td>
|
||||
|
@ -866,7 +866,7 @@ If you're new to Angular, continue with the
|
|||
[tutorial](tutorial "Tour of Heroes tutorial").
|
||||
You can skip the "Setup" step since you're already using the Angular CLI setup.
|
||||
|
||||
如果你刚刚开始使用 Angular,我们建议你遵循这个[教程](tutorial "《英雄指南》教程")。
|
||||
你可以跳过“环境设置”一章,因为你已经在使用 Angular-CLI 设置好环境了。
|
||||
如果你刚刚开始使用 Angular,请继续这个[教程](tutorial "《英雄指南》教程")。
|
||||
你可以跳过“环境设置”一章,因为你已经在使用 Angular CLI 设置好环境了。
|
||||
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,7 @@ _Reactive forms_ is an Angular technique for creating forms in a _reactive_ styl
|
|||
This guide explains reactive forms as you follow the steps to build a "Hero Detail Editor" form.
|
||||
|
||||
*响应式表单*是 Angular 中用*响应式*风格创建表单的技术。
|
||||
本章中,我们会在构建“英雄详情编辑器”的过程中,逐步讲解响应式表单的概念。
|
||||
本章会在构建“英雄详情编辑器”的过程中,逐步讲解响应式表单的概念。
|
||||
|
||||
{@a toc}
|
||||
|
||||
|
@ -57,7 +57,7 @@ With _reactive_ forms, you create a tree of Angular form control objects
|
|||
in the component class and bind them to native form control elements in the
|
||||
component template, using techniques described in this guide.
|
||||
|
||||
使用*响应式*表单,我们可以在组件中创建表单控件的对象树,并使用本章中传授的技巧把它们绑定到组件模板中的原生表单控件元素上。
|
||||
使用*响应式*表单,你可以在组件中创建表单控件的对象树,并使用本章中传授的技巧把它们绑定到组件模板中的原生表单控件元素上。
|
||||
|
||||
You create and manipulate form control objects directly in the
|
||||
component class. As the component class has immediate access to both the data
|
||||
|
@ -65,8 +65,8 @@ model and the form control structure, you can push data model values into
|
|||
the form controls and pull user-changed values back out. The component can
|
||||
observe changes in form control state and react to those changes.
|
||||
|
||||
我们可以在组件类中直接创建和维护表单控件对象。由于组件类可以同时访问数据模型和表单控件结构,
|
||||
因此我们可以把表单模型值的变化推送到表单控件中,并把变化后的值拉取回来。
|
||||
你可以在组件类中直接创建和维护表单控件对象。由于组件类可以同时访问数据模型和表单控件结构,
|
||||
因此你可以把表单模型值的变化推送到表单控件中,并把变化后的值拉取回来。
|
||||
组件可以监听表单控件状态的变化,并对此做出响应。
|
||||
|
||||
One advantage of working with form control objects directly is that value and validity updates
|
||||
|
@ -75,7 +75,7 @@ You won't encounter the timing issues that sometimes plague a template-driven fo
|
|||
and reactive forms can be easier to unit test.
|
||||
|
||||
直接使用表单控件对象的优点之一是值和有效性状态的更新[总是同步的,并且在你的控制之下](guide/reactive-forms#async-vs-sync "Async vs sync")。
|
||||
我们不会遇到时序问题,这个问题有时在模板驱动表单中会成为灾难。而且响应式表单更容易进行单元测试。
|
||||
你不会遇到时序问题,这个问题有时在模板驱动表单中会成为灾难。而且响应式表单更容易进行单元测试。
|
||||
|
||||
In keeping with the reactive paradigm, the component
|
||||
preserves the immutability of the _data model_,
|
||||
|
@ -100,21 +100,21 @@ but it does facilitate the reactive programming approach should you choose to us
|
|||
|
||||
_Template-driven_ forms, introduced in the [Template guide](guide/forms), take a completely different approach.
|
||||
|
||||
在[模板](guide/forms)一章我们介绍过的*模板驱动*表单,是一种完全不同的方式。
|
||||
在[模板](guide/forms)一章中介绍过的*模板驱动*表单,是一种完全不同的方式。
|
||||
|
||||
You place HTML form controls (such as `<input>` and `<select>`) in the component template and
|
||||
bind them to _data model_ properties in the component, using directives
|
||||
like `ngModel`.
|
||||
|
||||
我们把 HTML 表单控件(比如 `<input>` 和 `<select>`)放进组件模板中,并用 `ngModel` 等指令把它们绑定到组件中*数据模型*的属性上。
|
||||
你把 HTML 表单控件(比如 `<input>` 和 `<select>`)放进组件模板中,并用 `ngModel` 等指令把它们绑定到组件中*数据模型*的属性上。
|
||||
|
||||
You don't create Angular form control objects. Angular directives
|
||||
create them for you, using the information in your data bindings.
|
||||
You don't push and pull data values. Angular handles that for you with `ngModel`.
|
||||
Angular updates the mutable _data model_ with user changes as they happen.
|
||||
|
||||
我们不用自己创建 Angular 表单控件对象。Angular 指令会使用数据绑定中的信息创建它们。
|
||||
我们不用自己推送和拉取数据。Angular 使用 `ngModel` 来替你管理它们。
|
||||
你不用自己创建 Angular 表单控件对象。Angular 指令会使用数据绑定中的信息创建它们。
|
||||
你不用自己推送和拉取数据。Angular 使用 `ngModel` 来替你管理它们。
|
||||
当用户做出修改时,Angular 会据此更新可变的*数据模型*。
|
||||
|
||||
For this reason, the `ngModel` directive is not part of the ReactiveFormsModule.
|
||||
|
@ -141,8 +141,8 @@ In reactive forms, you create the entire form control tree in code.
|
|||
You can immediately update a value or drill down through the descendents of the parent form
|
||||
because all controls are always available.
|
||||
|
||||
使用响应式表单,我们会在代码中创建整个表单控件树。
|
||||
我们可以立即更新一个值或者深入到表单中的任意节点,因为所有的控件都始终是可用的。
|
||||
使用响应式表单,你会在代码中创建整个表单控件树。
|
||||
你可以立即更新一个值或者深入到表单中的任意节点,因为所有的控件都始终是可用的。
|
||||
|
||||
Template-driven forms delegate creation of their form controls to directives.
|
||||
To avoid "_changed after checked_" errors,
|
||||
|
@ -152,7 +152,7 @@ from within the component class.
|
|||
|
||||
模板驱动表单会委托指令来创建它们的表单控件。
|
||||
为了消除“检查完后又变化了”的错误,这些指令需要消耗一个以上的变更检测周期来构建整个控件树。
|
||||
这意味着在从组件类中操纵任何控件之前,我们都必须先等待一个节拍。
|
||||
这意味着在从组件类中操纵任何控件之前,你都必须先等待一个节拍。
|
||||
|
||||
For example, if you inject the form control with a `@ViewChild(NgForm)` query and examine it in the
|
||||
[`ngAfterViewInit` lifecycle hook](guide/lifecycle-hooks#afterview "Lifecycle hooks guide: AfterView"),
|
||||
|
@ -160,8 +160,8 @@ you'll discover that it has no children.
|
|||
You must wait a tick, using `setTimeout`, before you can
|
||||
extract a value from a control, test its validity, or set it to a new value.
|
||||
|
||||
比如,如果我们用 `@ViewChild(NgForm)` 查询来注入表单控件,并在[生命周期钩子 `ngAfterViewInit`](guide/lifecycle-hooks#afterview "Lifecycle hooks guide: AfterView")中检查它,就会发现它没有子控件。
|
||||
我们必须使用 `setTimeout` 等待一个节拍才能从控件中提取值、测试有效性,或把它设置为新值。
|
||||
比如,如果你用 `@ViewChild(NgForm)` 查询来注入表单控件,并在[生命周期钩子 `ngAfterViewInit`](guide/lifecycle-hooks#afterview "Lifecycle hooks guide: AfterView")中检查它,就会发现它没有子控件。
|
||||
你必须使用 `setTimeout` 等待一个节拍才能从控件中提取值、测试有效性,或把它设置为新值。
|
||||
|
||||
The asynchrony of template-driven forms also complicates unit testing.
|
||||
You must wrap your test block in `async()` or `fakeAsync()` to
|
||||
|
@ -169,7 +169,7 @@ avoid looking for values in the form that aren't there yet.
|
|||
With reactive forms, everything is available when you expect it to be.
|
||||
|
||||
模板驱动表单的异步性让单元测试也变得复杂化了。
|
||||
我们必须把测试代码包裹在 `async()` 或 `fakeAsync()` 中来解决要查阅的值尚不存在的情况。
|
||||
你必须把测试代码包裹在 `async()` 或 `fakeAsync()` 中来解决要查阅的值尚不存在的情况。
|
||||
使用响应式表单,在所期望的时机一切都是可用的。
|
||||
|
||||
### Which is better, reactive or template-driven?
|
||||
|
@ -190,12 +190,13 @@ The balance of this _reactive forms_ guide explores the _reactive_ paradigm and
|
|||
concentrates exclusively on reactive forms techniques.
|
||||
For information on _template-driven forms_, see the [_Forms_](guide/forms) guide.
|
||||
|
||||
在这章*响应式表单*中,我们只专注于*响应式*范式以及响应式表单技术的详情。
|
||||
在这章*响应式表单*中,只专注于*响应式*范式以及响应式表单技术的详情。
|
||||
要了解关于*模板驱动表单*的更多信息,参见[表单](guide/forms)一章。
|
||||
|
||||
In the next section, you'll set up your project for the reactive form demo.
|
||||
Then you'll learn about the [Angular form classes](guide/reactive-forms#essentials) and how to use them in a reactive form.
|
||||
|
||||
在下一节,我们将先准备一个响应式表单范例的项目,然后就可以开始学习[Angular 表单类](guide/reactive-forms#essentials),并在响应式表单中使用它们了。
|
||||
在下一节,你要先准备一个响应式表单范例的项目,然后就可以开始学习[Angular 表单类](guide/reactive-forms#essentials),并在响应式表单中使用它们了。
|
||||
|
||||
{@a setup}
|
||||
|
||||
|
@ -223,7 +224,7 @@ The focus of this guide is a reactive forms component that edits a hero.
|
|||
You'll need a `hero` class and some hero data.
|
||||
|
||||
本章的焦点是响应式表单组件以及编辑一个英雄。
|
||||
我们需要一个 `Hero` 类和一些英雄数据。
|
||||
你需要一个 `Hero` 类和一些英雄数据。
|
||||
|
||||
Using the CLI, generate a new class named `data-model`:
|
||||
|
||||
|
@ -279,7 +280,7 @@ Next, update the `HeroDetailComponent` class with a `FormControl`.
|
|||
a `FormControl` instance directly.
|
||||
|
||||
接下来,创建并导出一个带 `FormControl` 的 `HeroDetailComponent` 类。
|
||||
`FormControl` 是一个指令,它允许我们直接创建并管理一个 `FormControl` 实例。
|
||||
`FormControl` 是一个指令,它允许你直接创建并管理一个 `FormControl` 实例。
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-1.component.ts" region="v1" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
|
||||
|
||||
|
@ -288,7 +289,7 @@ a `FormControl` instance directly.
|
|||
Here you are creating a `FormControl` called `name`.
|
||||
It will be bound in the template to an HTML `input` box for the hero name.
|
||||
|
||||
这里我们创建了一个名叫 `name` 的 `FormControl`。
|
||||
这里你创建了一个名叫 `name` 的 `FormControl`。
|
||||
它将会绑定到模板中的一个 `input` 框,表示英雄的名字。
|
||||
|
||||
A `FormControl` constructor accepts three, optional arguments:
|
||||
|
@ -329,7 +330,7 @@ To let Angular know that this is the input that you want to
|
|||
associate to the `name` `FormControl` in the class,
|
||||
you need `[formControl]="name"` in the template on the `<input>`.
|
||||
|
||||
要让 Angular 知道我们希望把这个输入框关联到类中的 `FormControl` 型属性 `name`,我们需要在模板中的 `<input>` 上加一句 `[formControl]="name"`。
|
||||
要让 Angular 知道你希望把这个输入框关联到类中的 `FormControl` 型属性 `name`,就要在模板中的 `<input>` 上加一句 `[formControl]="name"`。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -427,7 +428,7 @@ tracks the value and validity state of a numerically indexed _array_ of `Abstrac
|
|||
|
||||
You'll learn more about these classes as you work through this guide.
|
||||
|
||||
随着本章的深入,我们将学到关于这三个类的更多知识。
|
||||
随着本章的深入,你将学到关于这些类的更多知识。
|
||||
|
||||
### Style the app
|
||||
|
||||
|
@ -436,7 +437,7 @@ You'll learn more about these classes as you work through this guide.
|
|||
You used bootstrap CSS classes in the template HTML of both the `AppComponent` and the `HeroDetailComponent`.
|
||||
Add the `bootstrap` _CSS stylesheet_ to the head of `styles.css`:
|
||||
|
||||
我们在 `AppComponent` 和 `HeroDetailComponent` 的模板中使用 Bootstrap 中的 CSS 类。请把 `bootstrap` 的*CSS 样式表文件*添加到 `index.html` 的 `head` 区。
|
||||
你在 `AppComponent` 和 `HeroDetailComponent` 的模板中使用 Bootstrap 中的 CSS 类。请把 `bootstrap` 的*CSS 样式表文件*添加到 `index.html` 的 `head` 区。
|
||||
|
||||
<code-example path="reactive-forms/src/styles.1.css" title="styles.css" linenums="false">
|
||||
|
||||
|
@ -461,7 +462,7 @@ them within a parent `FormGroup`.
|
|||
This is simple to do. To add a `FormGroup`, add it to the imports section
|
||||
of `hero-detail.component.ts`:
|
||||
|
||||
通常,如果有多个 *FormControl*,我们会希望把它们注册进一个父 `FormGroup` 中。这很容易。只要把它加入 `hero-detail.component.ts` 的 `import` 区就可以了。
|
||||
通常,如果有多个 *FormControl*,你会希望把它们注册进一个父 `FormGroup` 中。这很容易。只要把它加入 `hero-detail.component.ts` 的 `import` 区就可以了。
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-2.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts" linenums="false">
|
||||
|
||||
|
@ -478,7 +479,7 @@ In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follo
|
|||
Now that you've made changes in the class, they need to be reflected in the
|
||||
template. Update `hero-detail.component.html` by replacing it with the following.
|
||||
|
||||
现在我们改完了这个类,该把它映射到模板中了。把 `hero-detail.component.html` 改成这样:
|
||||
现在你改完了这个类,该把它映射到模板中了。把 `hero-detail.component.html` 改成这样:
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-2.component.html" region="basic-form" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
|
||||
|
||||
|
@ -511,7 +512,7 @@ in the class. This syntax tells Angular to look for the parent
|
|||
`FormGroup`, in this case `heroForm`, and then _inside_ that group
|
||||
to look for a `FormControl` called `name`.
|
||||
|
||||
由于现在有了一个 `FormGroup`,因此我们必须修改模板语法来把输入框关联到组件类中对应的 `FormControl` 上。
|
||||
由于现在有了一个 `FormGroup`,因此你必须修改模板语法来把输入框关联到组件类中对应的 `FormControl` 上。
|
||||
以前没有父 `FormGroup` 的时候,`[formControl]="name"` 也能正常工作,因为该指令可以独立工作,也就是说,不在 `FormGroup` 中时它也能用。
|
||||
有了 `FormGroup`,`name` 输入框就需要再添加一个语法 `formControlName=name`,以便让它关联到类中正确的 `FormControl` 上。
|
||||
这个语法告诉 Angular,查阅父 `FormGroup`(这里是 `heroForm`),然后在这个 `FormGroup` 中查阅一个名叫 `name` 的 `FormControl`。
|
||||
|
@ -569,7 +570,7 @@ Type into the _name_ input box and watch the keystokes appear in the JSON.
|
|||
|
||||
Great! You have the basics of a form.
|
||||
|
||||
真棒!我们有了一个基本版表单。
|
||||
真棒!你有了一个基本版表单。
|
||||
|
||||
In real life apps, forms get big fast.
|
||||
`FormBuilder` makes form development and maintenance easier.
|
||||
|
@ -586,11 +587,11 @@ In real life apps, forms get big fast.
|
|||
The `FormBuilder` class helps reduce repetition and
|
||||
clutter by handling details of control creation for you.
|
||||
|
||||
`FormBuilder` 类能通过处理控件创建的细节问题来帮我们减少重复劳动。
|
||||
`FormBuilder` 类能通过处理控件创建的细节问题来帮你减少重复劳动。
|
||||
|
||||
To use `FormBuilder`, you need to import it into `hero-detail.component.ts`:
|
||||
|
||||
要使用 `FormBuilder`,我们就要先把它导入到 `hero-detail.component.ts` 中:
|
||||
要使用 `FormBuilder`,你就要先把它导入到 `hero-detail.component.ts` 中:
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3a.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
|
||||
|
||||
|
@ -599,11 +600,11 @@ To use `FormBuilder`, you need to import it into `hero-detail.component.ts`:
|
|||
Use it now to refactor the `HeroDetailComponent` into something that's a little easier to read and write,
|
||||
by following this plan:
|
||||
|
||||
现在,我们遵循下列步骤用 `FormBuilder` 来把 `HeroDetailComponent` 重构得更加容易读写。
|
||||
现在,你要遵循下列步骤用 `FormBuilder` 来把 `HeroDetailComponent` 重构得更加容易读写。
|
||||
|
||||
* Explicitly declare the type of the `heroForm` property to be `FormGroup`; you'll initialize it later.
|
||||
|
||||
明确把 `heroForm` 属性的类型声明为 `FormGroup`,稍后我们会初始化它。
|
||||
明确把 `heroForm` 属性的类型声明为 `FormGroup`,稍后你会初始化它。
|
||||
|
||||
* Inject a `FormBuilder` into the constructor.
|
||||
|
||||
|
@ -674,7 +675,7 @@ Reactive validators are simple, composable functions.
|
|||
Configuring validation is harder in template-driven forms where you must wrap validators in a directive.
|
||||
|
||||
响应式验证器是一些简单、可组合的函数。
|
||||
在模板驱动表单中配置验证器有些困难,因为我们必须把验证器包装进指令中。
|
||||
在模板驱动表单中配置验证器有些困难,因为你必须把验证器包装进指令中。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -702,7 +703,7 @@ Type into the input box to see the status change from `INVALID` to `VALID`.
|
|||
|
||||
In a real app, you'd replace the diagnosic message with a user-friendly experience.
|
||||
|
||||
在真实的应用中,我们要把这些诊断信息替换成用户友好的信息。
|
||||
在真实的应用中,你要把这些诊断信息替换成用户友好的信息。
|
||||
|
||||
Using `Validators.required` is optional for the rest of the guide.
|
||||
It remains in each of the following examples with the same configuration.
|
||||
|
@ -726,7 +727,7 @@ A hero has an address, a super power and sometimes a sidekick too.
|
|||
The address has a state property. The user will select a state with a `<select>` box and you'll populate
|
||||
the `<option>` elements with states. So import `states` from `data-model.ts`.
|
||||
|
||||
住址中有一个所在州属性,用户将会从 `<select>` 框中选择一个州,我们会用 `<option>` 元素渲染各个州。我们从 `data-model.ts` 中导入 `states`(州列表)。
|
||||
住址中有一个所在州属性,用户将会从 `<select>` 框中选择一个州,你会用 `<option>` 元素渲染各个州。从 `data-model.ts` 中导入 `states`(州列表)。
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-4.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
|
||||
|
||||
|
@ -773,7 +774,7 @@ and a checkbox for the `sidekick`.
|
|||
You must bind the option's value property with `[value]="state"`.
|
||||
If you do not bind the value, the select shows the first option from the data model.
|
||||
|
||||
我们要用 `[value]="state"` 来绑定选项的 `value` 属性。
|
||||
你要用 `[value]="state"` 来绑定选项的 `value` 属性。
|
||||
如果不绑定这个值,这个选择框就会显示来自数据模型中的第一个选项。
|
||||
|
||||
The component _class_ defines control properties without regard for their representation in the template.
|
||||
|
@ -782,7 +783,7 @@ You tie these controls to the template HTML elements in the same way,
|
|||
specifying the `FormControl` name with the `formControlName` directive.
|
||||
|
||||
组件*类*定义了控件属性而不用管它们在模板中的表现形式。
|
||||
我们可以像定义 `name` 控件一样定义 `state`、`power` 和 `sidekick` 控件,并用 `formControlName` 指令来指定 `FormControl` 的名字。
|
||||
你可以像定义 `name` 控件一样定义 `state`、`power` 和 `sidekick` 控件,并用 `formControlName` 指令来指定 `FormControl` 的名字。
|
||||
|
||||
See the API reference for more information about
|
||||
[radio buttons](api/forms/RadioControlValueAccessor "API: RadioControlValueAccessor"),
|
||||
|
@ -806,16 +807,17 @@ Nesting groups and controls in this way allows you to
|
|||
mirror the hierarchical structure of the data model
|
||||
and helps track validation and state for related sets of controls.
|
||||
|
||||
这个表单变得越来越大、越来越笨重。我们可以把一些相关的 `FormControl` 组织到多级 `FormGroup` 中。
|
||||
这个表单变得越来越大、越来越笨重。
|
||||
你可以把一些相关的 `FormControl` 组织到多级 `FormGroup` 中。
|
||||
`street`、`city`、`state` 和 `zip` 属性就可以作为一个名叫 `address` 的 `FormGroup`。
|
||||
用这种方式,多级表单组和控件可以让我们轻松地映射多层结构的数据模型,以便帮助我们跟踪这组相关控件的有效性和状态。
|
||||
用这种方式,多级表单组和控件可以让你轻松地映射多层结构的数据模型,以帮你跟踪这组相关控件的有效性和状态。
|
||||
|
||||
You used the `FormBuilder` to create one `FormGroup` in this component called `heroForm`.
|
||||
Let that be the parent `FormGroup`.
|
||||
Use `FormBuilder` again to create a child `FormGroup` that encapsulates the address controls;
|
||||
assign the result to a new `address` property of the parent `FormGroup`.
|
||||
|
||||
我们用 `FormBuilder` 在这个名叫 `heroForm` 的组件中创建一个 `FormGroup`,并把它用作父 `FormGroup`。
|
||||
你用 `FormBuilder` 在这个名叫 `heroForm` 的组件中创建一个 `FormGroup`,并把它用作父 `FormGroup`。
|
||||
再次使用 `FormBuilder` 创建一个子级 `FormGroup`,其中包括这些住址控件。把结果赋值给父 `FormGroup` 中新的 `address` 属性。
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-5.component.ts" region="v5" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
|
||||
|
@ -825,7 +827,7 @@ assign the result to a new `address` property of the parent `FormGroup`.
|
|||
You’ve changed the structure of the form controls in the component class;
|
||||
you must make corresponding adjustments to the component template.
|
||||
|
||||
我们已经修改了组件类中表单控件的结构,还必须对组件模板进行相应的调整。
|
||||
你已经修改了组件类中表单控件的结构,还必须对组件模板进行相应的调整。
|
||||
|
||||
In `hero-detail.component.html`, wrap the address-related `FormControls` in a `div`.
|
||||
Add a `formGroupName` directive to the `div` and bind it to `"address"`.
|
||||
|
@ -857,7 +859,7 @@ with the nested address `FormGroup`:
|
|||
Great! You’ve made a group and you can see that the template
|
||||
and the form model are talking to one another.
|
||||
|
||||
真棒!我们制作了一个控件组,并且可以看到模板和表单模型已经能彼此通讯了。
|
||||
真棒!你制作了一个控件组,并且可以看到模板和表单模型已经能彼此通讯了。
|
||||
|
||||
{@a properties}
|
||||
|
||||
|
@ -868,16 +870,16 @@ and the form model are talking to one another.
|
|||
At the moment, you're dumping the entire form model onto the page.
|
||||
Sometimes you're interested only in the state of one particular `FormControl`.
|
||||
|
||||
此刻,我们把整个表单模型展示在了页面里。
|
||||
但有时我们可能只关心一个特定 `FormControl` 的状态。
|
||||
此刻,你把整个表单模型展示在了页面里。
|
||||
但有时你可能只关心一个特定 `FormControl` 的状态。
|
||||
|
||||
You can inspect an individual `FormControl` within a form by extracting it with the `.get()` method.
|
||||
You can do this _within_ the component class or display it on the
|
||||
page by adding the following to the template,
|
||||
immediately after the `{{form.value | json}}` interpolation as follows:
|
||||
|
||||
我们可以使用 `.get()` 方法来提取表单中一个单独 `FormControl` 的状态。
|
||||
我们可以在组件类中这么做,或者通过往模板中添加下列代码来把它显示在页面中,就添加在 `{{form.value | json}}` 插值表达式的紧后面:
|
||||
你可以使用 `.get()` 方法来提取表单中一个单独 `FormControl` 的状态。
|
||||
你可以在组件类中这么做,或者通过往模板中添加下列代码来把它显示在页面中,就添加在 `{{form.value | json}}` 插值表达式的紧后面:
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-5.component.html" region="inspect-value" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
|
||||
|
||||
|
@ -894,7 +896,7 @@ To get the state of a `FormControl` that’s inside a `FormGroup`, use dot notat
|
|||
You can use this technique to display _any_ property of a `FormControl`
|
||||
such as one of the following:
|
||||
|
||||
我们可以使用此技术来显示 `FromControl` 的*任意*属性,代码如下:
|
||||
你可以使用此技术来显示 `FromControl` 的*任意*属性,代码如下:
|
||||
|
||||
<style>
|
||||
td, th {vertical-align: top}
|
||||
|
@ -1062,7 +1064,7 @@ The _form_ and _data_ model structures need not match exactly.
|
|||
You often present a subset of the _data model_ on a particular screen.
|
||||
But it makes things easier if the shape of the _form model_ is close to the shape of the _data model_.
|
||||
|
||||
*表单模型*和*数据模型*的结构并不需要精确匹配。在一个特定的屏幕上,我们通常只会展现*数据模型*的一个子集。
|
||||
*表单模型*和*数据模型*的结构并不需要精确匹配。在一个特定的屏幕上,你通常只会展现*数据模型*的一个子集。
|
||||
但是*表单模型*的形态越接近*数据模型*,事情就会越简单。
|
||||
|
||||
In this `HeroDetailComponent`, the two models are quite close.
|
||||
|
@ -1091,7 +1093,7 @@ There are two significant differences between these models:
|
|||
|
||||
1. The `Hero` has an `id`. The form model does not because you generally don't show primary keys to users.
|
||||
|
||||
`Hero` 有一个 `id`。表单模型中则没有,因为我们通常不会把主键展示给用户。
|
||||
`Hero` 有一个 `id`。表单模型中则没有,因为你通常不会把主键展示给用户。
|
||||
|
||||
1. The `Hero` has an array of addresses. This form model presents only one address,
|
||||
a choice [revisited below](guide/reactive-forms#form-array "Form arrays").
|
||||
|
@ -1101,7 +1103,7 @@ a choice [revisited below](guide/reactive-forms#form-array "Form arrays").
|
|||
Nonetheless, the two models are pretty close in shape and you'll see in a moment how this alignment facilitates copying the _data model_ properties
|
||||
to the _form model_ with the `patchValue` and `setValue` methods.
|
||||
|
||||
虽然如此,这两个模型的形态仍然是非常接近的,我们很快就会看到如何用 `patchValue` 和 `setValue` 方法来把*数据模型*拷贝到*表单模型*中。
|
||||
虽然如此,这两个模型的形态仍然是非常接近的,你很快就会看到如何用 `patchValue` 和 `setValue` 方法来把*数据模型*拷贝到*表单模型*中。
|
||||
|
||||
Take a moment to refactor the _address_ `FormGroup` definition for brevity and clarity as follows:
|
||||
|
||||
|
@ -1113,7 +1115,7 @@ Take a moment to refactor the _address_ `FormGroup` definition for brevity and c
|
|||
|
||||
Also be sure to update the import from `data-model` so you can reference the `Hero` and `Address` classes:
|
||||
|
||||
为了确保从 `data-model` 中导入,我们可以引用 `Hero` 和 `Address` 类:
|
||||
为了确保从 `data-model` 中导入,你可以引用 `Hero` 和 `Address` 类:
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="import-address" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
|
||||
|
||||
|
@ -1129,8 +1131,8 @@ Previously you created a control and initialized its value at the same time.
|
|||
You can also initialize or reset the values _later_ with the
|
||||
`setValue` and `patchValue` methods.
|
||||
|
||||
以前,我们创建了控件,并同时初始化它的值。
|
||||
我们也可以稍后用 `setValue` 和 `patchValue` 来初始化或重置这些值。
|
||||
以前,你创建了控件,并同时初始化它的值。
|
||||
你也可以稍后用 `setValue` 和 `patchValue` 来初始化或重置这些值。
|
||||
|
||||
### _setValue_
|
||||
|
||||
|
@ -1139,7 +1141,7 @@ You can also initialize or reset the values _later_ with the
|
|||
With **`setValue`**, you assign _every_ form control value _at once_
|
||||
by passing in a data object whose properties exactly match the _form model_ behind the `FormGroup`.
|
||||
|
||||
借助**`setValue`**,我们可以*立即*设置*每个*表单控件的值,只要把与*表单模型*的属性精确匹配的数据模型传进去就可以了。
|
||||
借助**`setValue`**,你可以*立即*设置*每个*表单控件的值,只要把与*表单模型*的属性精确匹配的数据模型传进去就可以了。
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="set-value" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
|
||||
|
||||
|
@ -1155,7 +1157,7 @@ error messages if you have a typo or if you've nested controls incorrectly.
|
|||
`patchValue` will fail silently.
|
||||
|
||||
它不会接受一个与 FormGroup 结构不同或缺少表单组中任何一个控件的数据对象。
|
||||
这种方式下,如果我们有什么拼写错误或控件嵌套的不正确,它就能返回一些有用的错误信息。
|
||||
这种方式下,如果你有什么拼写错误或控件嵌套的不正确,它就能返回一些有用的错误信息。
|
||||
`patchValue` 会默默地失败。
|
||||
|
||||
On the other hand,`setValue` will catch
|
||||
|
@ -1171,7 +1173,7 @@ because its shape is similar to the component's `FormGroup` structure.
|
|||
You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all.
|
||||
This explains the conditional setting of the `address` property in the data object argument:
|
||||
|
||||
我们现在只能显示英雄的第一个住址,不过我们还必须考虑 `hero` 完全没有住址的可能性。
|
||||
你现在只能显示英雄的第一个住址,不过你还必须考虑 `hero` 完全没有住址的可能性。
|
||||
下面的例子解释了如何在数据对象参数中对 `address` 属性进行有条件的设置:
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="set-value-address" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
|
||||
|
@ -1185,7 +1187,7 @@ This explains the conditional setting of the `address` property in the data obje
|
|||
With **`patchValue`**, you can assign values to specific controls in a `FormGroup`
|
||||
by supplying an object of key/value pairs for just the controls of interest.
|
||||
|
||||
借助**`patchValue`**,我们可以通过提供一个只包含要更新的控件的键值对象来把值赋给 `FormGroup` 中的指定控件。
|
||||
借助**`patchValue`**,你可以通过提供一个只包含要更新的控件的键值对象来把值赋给 `FormGroup` 中的指定控件。
|
||||
|
||||
This example sets only the form's `name` control.
|
||||
|
||||
|
@ -1199,7 +1201,7 @@ With **`patchValue`** you have more flexibility to cope with wildly divergent da
|
|||
But unlike `setValue`, `patchValue` cannot check for missing control
|
||||
values and does not throw helpful errors.
|
||||
|
||||
借助**`patchValue`**,我们可以更灵活地解决数据模型和表单模型之间的差异。
|
||||
借助**`patchValue`**,你可以更灵活地解决数据模型和表单模型之间的差异。
|
||||
但是和 `setValue` 不同,`patchValue` 不会检查缺失的控件值,并且不会抛出有用的错误信息。
|
||||
|
||||
### When to set form model values (_ngOnChanges_)
|
||||
|
@ -1209,7 +1211,7 @@ values and does not throw helpful errors.
|
|||
Now you know _how_ to set the _form model_ values. But _when_ do you set them?
|
||||
The answer depends upon when the component gets the _data model_ values.
|
||||
|
||||
现在,我们已经知道了*如何*设置*表单模型*的值,但是*什么时候*设置它门呢?
|
||||
现在,你已经知道了*如何*设置*表单模型*的值,但是*什么时候*设置它门呢?
|
||||
答案取决于组件何时得到*数据模型*的值。
|
||||
|
||||
The `HeroDetailComponent` in this reactive forms sample is nested within a _master/detail_ `HeroListComponent` ([discussed below](guide/reactive-forms#hero-list)).
|
||||
|
@ -1232,7 +1234,7 @@ hook, which Angular calls whenever the input `hero` property changes
|
|||
as the following steps demonstrate.
|
||||
|
||||
这种方式下,每当用户选择一个新英雄时,`HeroDetailComponent` 中的 `hero` 值就会发生变化。
|
||||
我们可以在[ngOnChanges](guide/lifecycle-hooks#onchanges)钩子中调用 `setValue`,就像例子中所演示的那样,
|
||||
你可以在[ngOnChanges](guide/lifecycle-hooks#onchanges)钩子中调用 `setValue`,就像例子中所演示的那样,
|
||||
每当输入属性 `hero` 发生变化时,Angular 就会调用它。
|
||||
|
||||
First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`.
|
||||
|
@ -1268,8 +1270,8 @@ control values from the previous hero are cleared and
|
|||
status flags are restored to the _pristine_ state.
|
||||
You could call `reset` at the top of `ngOnChanges` like this.
|
||||
|
||||
我们应该在更换英雄的时候重置表单,以便来自前一个英雄的控件值被清除,并且其状态被恢复为 `pristine`(原始)状态。
|
||||
我们可以在 `ngOnChanges` 的顶部调用 `reset`,就像这样:
|
||||
你应该在更换英雄的时候重置表单,以便来自前一个英雄的控件值被清除,并且其状态被恢复为 `pristine`(原始)状态。
|
||||
你可以在 `ngOnChanges` 的顶部调用 `reset`,就像这样:
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="reset" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
|
||||
|
||||
|
@ -1279,7 +1281,7 @@ The `reset` method has an optional `state` value so you can reset the flags _and
|
|||
Internally, `reset` passes the argument to `setValue`.
|
||||
A little refactoring and `ngOnChanges` becomes this:
|
||||
|
||||
`reset` 方法有一个可选的 `state` 值,让我们能在重置状态的同时*顺便设置*控件的值。
|
||||
`reset` 方法有一个可选的 `state` 值,让你能在重置状态的同时*顺便设置*控件的值。
|
||||
在内部实现上,`reset` 会把该参数传给了 `setValue`。
|
||||
略微重构之后,`ngOnChanges` 会变成这样:
|
||||
|
||||
|
@ -1342,7 +1344,7 @@ Then return here to learn about _form array_ properties.
|
|||
|
||||
如果你正在随着本教程写代码,可以基于[下面显示的代码](guide/reactive-forms#source-code "Reactive Forms source code")来创建相应的文件。
|
||||
注意,`hero-list.component.ts` 从 `rxjs` 中导入了 `Observable` 和 `finally`,而 `hero.service.ts` 导入了 `Observable`、`of` 和 `delay`。
|
||||
接下来我们回到正轨,继续学习*表单数组*属性。
|
||||
然后回来继续学习*表单数组*的属性。
|
||||
|
||||
{@a form-array}
|
||||
|
||||
|
@ -1359,7 +1361,7 @@ A `FormGroup` is a named object whose property values are `FormControls` and oth
|
|||
Sometimes you need to present an arbitrary number of controls or groups.
|
||||
For example, a hero may have zero, one, or any number of addresses.
|
||||
|
||||
有时我们得表示任意数量的控件或控件组。
|
||||
有时你需要表示任意数量的控件或控件组。
|
||||
比如,一个英雄可能拥有 0、1 或任意数量的住址。
|
||||
|
||||
The `Hero.addresses` property is an array of `Address` instances.
|
||||
|
@ -1380,7 +1382,7 @@ To get access to the `FormArray` class, import it into `hero-detail.component.ts
|
|||
|
||||
To _work_ with a `FormArray` you do the following:
|
||||
|
||||
要想使用 `FormArray`,我们要这么做:
|
||||
要想使用 `FormArray`,你要这么做:
|
||||
|
||||
1. Define the items (`FormControls` or `FormGroups`) in the array.
|
||||
|
||||
|
@ -1397,12 +1399,12 @@ To _work_ with a `FormArray` you do the following:
|
|||
In this guide, you define a `FormArray` for `Hero.addresses` and
|
||||
let the user add or modify addresses (removing addresses is your homework).
|
||||
|
||||
在本章中,我们为 `Hero.addresses` 定义了一个 `FormArray`,并且让用户添加或修改这些住址(移除住址功能请课后自行实现)。
|
||||
在本章中,你为 `Hero.addresses` 定义了一个 `FormArray`,并且让用户添加或修改这些住址(移除住址功能请课后自行实现)。
|
||||
|
||||
You’ll need to redefine the form model in the `HeroDetailComponent` constructor,
|
||||
which currently only displays the first hero address in an _address_ `FormGroup`.
|
||||
|
||||
我们需要在 `HeroDetailComponent` 的构造函数中重新定义表单模型,它现在只用 `FormGroup` 显示第一个英雄住址。
|
||||
你需要在 `HeroDetailComponent` 的构造函数中重新定义表单模型,它现在只用 `FormGroup` 显示第一个英雄住址。
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="address-form-group" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
|
||||
|
||||
|
@ -1429,7 +1431,7 @@ Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` de
|
|||
Changing the form control name from `address` to `secretLairs` drives home an important point:
|
||||
the _form model_ doesn't have to match the _data model_.
|
||||
|
||||
把表单的控件名从 `address` 改为 `secretLairs` 让我们遇到了一个重要问题:*表单模型*与*数据模型*不再匹配了。
|
||||
把表单的控件名从 `address` 改为 `secretLairs` 时遇到了一个重要问题:*表单模型*与*数据模型*不再匹配了。
|
||||
|
||||
Obviously there has to be a relationship between the two.
|
||||
But it can be anything that makes sense within the application domain.
|
||||
|
@ -1455,7 +1457,7 @@ The default form displays a nameless hero with no addresses.
|
|||
You need a method to populate (or repopulate) the _secretLairs_ with actual hero addresses whenever
|
||||
the parent `HeroListComponent` sets the `HeroDetailComponent.hero` input property to a new `Hero`.
|
||||
|
||||
我们需要一个方法来用实际英雄的地址填充(或重新填充)`secretLairs`,
|
||||
你需要一个方法来用实际英雄的地址填充(或重新填充)`secretLairs`,
|
||||
而不用管父组件 `HeroListComponent` 何时把输入属性 `HeroDetailComponent.hero` 设置为一个新的 `Hero`。
|
||||
|
||||
The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
|
||||
|
@ -1470,8 +1472,8 @@ initialized by an array of hero address `FormGroups`.
|
|||
Notice that you replace the previous `FormArray` with the **`FormGroup.setControl` method**, not with `setValue`.
|
||||
You're replacing a _control_, not the _value_ of a control.
|
||||
|
||||
注意,我们使用**`FormGroup.setControl` 方法**,而不是 `setValue` 方法来设置前一个 `FormArray`。
|
||||
我们所要替换的是*控件*,而不是控件的*值*。
|
||||
注意,你使用**`FormGroup.setControl` 方法**,而不是 `setValue` 方法来设置前一个 `FormArray`。
|
||||
你所要替换的是*控件*,而不是控件的*值*。
|
||||
|
||||
Notice also that the _secretLairs_ `FormArray` contains **`FormGroups`**, not `Addresses`.
|
||||
|
||||
|
@ -1503,7 +1505,7 @@ The current HTML template displays a single _address_ `FormGroup`.
|
|||
Revise it to display zero, one, or more of the hero's _address_ `FormGroups`.
|
||||
|
||||
当前 HTML 模板显示单个的地址 `FormGroup`。
|
||||
我们要把它修改成能显示 0、1 或更多个表示英雄地址的 `FormGroup`。
|
||||
要把它修改成能显示 0、1 或更多个表示英雄地址的 `FormGroup`。
|
||||
|
||||
This is mostly a matter of wrapping the previous template HTML for an address in a `<div>` and
|
||||
repeating that `<div>` with `*ngFor`.
|
||||
|
@ -1531,7 +1533,7 @@ Each control is an _address_ `FormGroup`, exactly what the previous (now repeate
|
|||
You'll re-use that index to compose a unique label for each address.
|
||||
|
||||
每个被重复渲染的 `FormGroup` 都需要一个独一无二的 `formGroupName`,它必须是 `FormGroup` 在这个 `FormArray` 中的索引。
|
||||
我们将复用这个索引,以便为每个地址组合出一个独一无二的标签。
|
||||
你将复用这个索引,以便为每个地址组合出一个独一无二的标签。
|
||||
|
||||
Here's the skeleton for the _secret lairs_ section of the HTML template:
|
||||
|
||||
|
@ -1579,10 +1581,10 @@ might do something like save the current changes.
|
|||
You do not want to save changes when the user clicks the _Add a Secret Lair_ button.
|
||||
|
||||
务必确保**添加了 `type="button"` 属性**。
|
||||
事实上,我们应该总是指定按钮的 `type`。
|
||||
事实上,你应该总是指定按钮的 `type`。
|
||||
如果不明确指定类型,按钮的默认类型就是“submit”(提交)。
|
||||
当我们稍后添加了*表单提交*的动作时,每个“submit”按钮都是触发一次提交操作,而它将可能会做一些处理,比如保存当前的修改。
|
||||
我们显然不会希望每当用户点击“Add a Secret Lair”按钮时就保存一次。
|
||||
当你稍后添加了*表单提交*的动作时,每个“submit”按钮都是触发一次提交操作,而它将可能会做一些处理,比如保存当前的修改。
|
||||
你显然不会希望每当用户点击“Add a Secret Lair”按钮时就保存一次。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1594,7 +1596,7 @@ Back in the browser, select the hero named "Magneta".
|
|||
"Magneta" doesn't have an address, as you can see in the diagnostic JSON at the bottom of the form.
|
||||
|
||||
回到浏览器中,选择名叫“Magneta”的英雄。
|
||||
"Magneta"没有地址,我们会在表单底部的诊断用 JSON 中看到这一点。
|
||||
"Magneta"没有地址,你会在表单底部的诊断用 JSON 中看到这一点。
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/addresses-array.png" alt="JSON output of addresses array">
|
||||
|
@ -1632,13 +1634,13 @@ Fortunately, you can learn about such changes by subscribing to one of the form
|
|||
that raises a change event.
|
||||
|
||||
当用户修改英雄的*名字*或*秘密小屋*时,Angular*并不会*调用 `ngOnChanges`。
|
||||
幸运的是,我们可以通过订阅表单控件的属性之一来了解这些变化,此属性会发出变更通知。
|
||||
幸运的是,你可以通过订阅表单控件的属性之一来了解这些变化,此属性会发出变更通知。
|
||||
|
||||
These are properties, such as `valueChanges`, that return an RxJS `Observable`.
|
||||
You don't need to know much about RxJS `Observable` to monitor form control values.
|
||||
|
||||
有一些属性,比如 `valueChanges`,可以返回一个 RxJS 的 `Observable` 对象。
|
||||
要监听控件值的变化,我们并不需要对 RxJS 的 `Observable` 了解更多。
|
||||
要监听控件值的变化,你并不需要对 RxJS 的 `Observable` 了解更多。
|
||||
|
||||
Add the following method to log changes to the value of the _name_ `FormControl`.
|
||||
|
||||
|
@ -1670,7 +1672,7 @@ Return to the browser, select a hero (e.g, "Magneta"), and start typing in the _
|
|||
You should see a new name in the log after each keystroke.
|
||||
|
||||
返回浏览器,选择一个英雄(比如“Magneta”),并开始在*姓名*输入框中键入。
|
||||
我们会看到,每次按键都会记录一个新名字。
|
||||
你会看到,每次按键都会记录一个新名字。
|
||||
|
||||
### When to use it
|
||||
|
||||
|
@ -1695,8 +1697,8 @@ In a real app, you'd also be able to revert unsaved changes and resume editing.
|
|||
After you implement both features in this section, the form will look like this:
|
||||
|
||||
`HeroDetailComponent` 捕获了用户输入,但没有用它做任何事。
|
||||
在真实的应用中,我们可能要保存这些英雄的变化。
|
||||
在真实的应用中,我们还要能丢弃未保存的变更,然后继续编辑。
|
||||
在真实的应用中,你可能要保存这些英雄的变化。
|
||||
在真实的应用中,你还要能丢弃未保存的变更,然后继续编辑。
|
||||
在实现完本节的这些特性之后,表单是这样的:
|
||||
|
||||
<figure>
|
||||
|
@ -1722,7 +1724,7 @@ So you create a new `hero` from a combination of original hero values (the `hero
|
|||
and deep copies of the changed form model values, using the `prepareSaveHero` helper.
|
||||
|
||||
原始的 `hero` 中有一些保存之前的值,用户的修改仍然是在*表单模型*中。
|
||||
所以我们要根据原始英雄(根据 `hero.id` 找到它)的值组合出一个新的 `hero` 对象,并用 `prepareSaveHero` 助手来深层复制变化后的模型值。
|
||||
所以你要根据原始英雄(根据 `hero.id` 找到它)的值组合出一个新的 `hero` 对象,并用 `prepareSaveHero` 助手来深层复制变化后的模型值。
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail.component.ts" region="prepare-save-hero" title="src/app/hero-detail/hero-detail.component.ts (prepareSaveHero)" linenums="false">
|
||||
|
||||
|
@ -1739,7 +1741,7 @@ the addresses in `saveHero.addresses` array would be the same objects
|
|||
as the lairs in the `formModel.secretLairs`.
|
||||
A user's subsequent changes to a lair street would mutate an address street in the `saveHero`.
|
||||
|
||||
我们已经把 `formModel.secretLairs` 赋值给了 `saveHero.addresses`(参见注释掉的部分),
|
||||
你已经把 `formModel.secretLairs` 赋值给了 `saveHero.addresses`(参见注释掉的部分),
|
||||
`saveHero.addresses` 数组中的地址和 `formModel.secretLairs` 中的会是同一个对象。
|
||||
用户随后对小屋所在街道的修改将会改变 `saveHero` 中的街道地址。
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -74,7 +74,7 @@ your website. The attack isn't limited to `<script>` tags—many elements an
|
|||
DOM allow code execution, for example, `<img onerror="...">` and `<a href="javascript:...">`. If
|
||||
attacker-controlled data enters the DOM, expect security vulnerabilities.
|
||||
|
||||
为了防范 XSS 攻击,我们必须阻止恶意代码进入 DOM。比如,如果某个攻击者能骗我们把 `<script>` 标签插入到 DOM,就可以在我们的网站上运行任何代码。
|
||||
为了防范 XSS 攻击,你必须阻止恶意代码进入 DOM。比如,如果某个攻击者能骗你把 `<script>` 标签插入到 DOM,就可以在你的网站上运行任何代码。
|
||||
除了 `<script>`,攻击者还可以使用很多 DOM 元素和属性来执行代码,比如 `<img onerror="...">`、`<a href="javascript:...">`。
|
||||
如果攻击者所控制的数据混进了 DOM,就会导致安全漏洞。
|
||||
|
||||
|
@ -279,7 +279,7 @@ Normally, Angular automatically sanitizes the URL, disables the dangerous code,
|
|||
in development mode, logs this action to the console. To prevent
|
||||
this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
|
||||
|
||||
通常,Angular 会自动无害化这个 URL 并禁止危险的代码。为了防止这种行为,我们可以调用 `bypassSecurityTrustUrl` 把这个 URL 值标记为一个可信任的 URL:
|
||||
通常,Angular 会自动无害化这个 URL 并禁止危险的代码。为了防止这种行为,可以调用 `bypassSecurityTrustUrl` 把这个 URL 值标记为一个可信任的 URL:
|
||||
|
||||
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" title="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
|
||||
|
||||
|
@ -296,9 +296,9 @@ context, because an untrusted source can, for example, smuggle in file downloads
|
|||
could execute. So call a method on the controller to construct a trusted video URL, which causes
|
||||
Angular to allow binding into `<iframe src>`:
|
||||
|
||||
如果需要把用户输入转换为一个可信任的值,我们可以很方便的在控制器方法中处理。下面的模板允许用户输入一个 YouTube 视频的 ID,
|
||||
如果需要把用户输入转换为一个可信任的值,可以在控制器方法中处理。下面的模板允许用户输入一个 YouTube 视频的 ID,
|
||||
然后把相应的视频加载到 `<iframe>` 中。`<iframe src>` 是一个“资源 URL”的安全环境,因为不可信的源码可能作为文件下载到本地,被毫无防备的用户执行。
|
||||
所以我们要调用一个控制器方法来构造一个新的、可信任的视频 URL,然后把它绑定到 `<iframe src>`。
|
||||
所以要调用一个控制器方法来构造一个新的、可信任的视频 URL,然后把它绑定到 `<iframe src>`。
|
||||
|
||||
<code-example path="security/src/app/bypass-security.component.html" linenums="false" title="src/app/bypass-security.component.html (iframe)" region="iframe">
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
Your app should be able to make the browser title bar say whatever you want it to say.
|
||||
This cookbook explains how to do it.
|
||||
|
||||
应用程序应该能让浏览器标题栏显示我们想让它显示的内容。本*烹饪宝典*解释怎么做。
|
||||
应用程序应该能让浏览器标题栏显示你想让它显示的内容。本*烹饪宝典*解释怎么做。
|
||||
|
||||
See the <live-example name="set-document-title"></live-example>.
|
||||
|
||||
|
@ -31,7 +31,7 @@ Sorry but that won't work.
|
|||
The root component of the application is an element contained within the `<body>` tag.
|
||||
The HTML `<title>` is in the document `<head>`, outside the body, making it inaccessible to Angular data binding.
|
||||
|
||||
抱歉,这样不行。我们应用程序的根组件是一个包含在 `<body>` 标签里的元素。该 HTML 的 `<title>` 在文档的 `<head>` 元素里,在 `<body>` 之外,Angular 的数据绑定无法访问到它。
|
||||
抱歉,这样不行。应用程序的根组件是一个包含在 `<body>` 标签里的元素。该 HTML 的 `<title>` 在文档的 `<head>` 元素里,在 `<body>` 之外,Angular 的数据绑定无法访问到它。
|
||||
|
||||
You could grab the browser `document` object and set the title manually.
|
||||
That's dirty and undermines your chances of running the app outside of a browser someday.
|
||||
|
@ -72,13 +72,13 @@ for getting and setting the current HTML document title:
|
|||
|
||||
You can inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
|
||||
|
||||
我们来把 `Title` 服务注入到根组件 `AppComponent`,并暴露出可供绑定的 `setTitle` 方法让别人来调用该服务:
|
||||
你可以把 `Title` 服务注入到根组件 `AppComponent`,并暴露出可供绑定的 `setTitle` 方法让别人来调用该服务:
|
||||
|
||||
<code-example path="set-document-title/src/app/app.component.ts" region="class" title="src/app/app.component.ts (class)" linenums="false"></code-example>
|
||||
|
||||
Bind that method to three anchor tags and voilà!
|
||||
|
||||
我们把这个方法绑定到三个 A 标签,瞧瞧!
|
||||
把这个方法绑定到三个 A 标签,瞧瞧!
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/set-document-title/set-title-anim.gif" alt="Set title">
|
||||
|
@ -102,12 +102,12 @@ Here's the complete solution:
|
|||
|
||||
Generally you want to provide application-wide services in the root application component, `AppComponent`.
|
||||
|
||||
我们通常会推荐在应用程序的根组件 `AppComponent` 中提供应用程序级的服务。
|
||||
你通常会在应用程序的根组件 `AppComponent` 中提供应用程序级的服务。
|
||||
|
||||
This cookbook recommends registering the title service during bootstrapping,
|
||||
a location you reserve for configuring the runtime Angular environment.
|
||||
|
||||
但这里,我们推荐在引导过程中注册这个 Title 服务,这个位置是为设置 Angular 运行环境而保留的。
|
||||
但这里,要在引导过程中注册这个 Title 服务,这个位置是为你设置 Angular 运行环境而保留的。
|
||||
|
||||
That's exactly what you're doing.
|
||||
The `Title` service is part of the Angular *browser platform*.
|
||||
|
@ -117,4 +117,4 @@ the concept of a "document title" for that specific platform.
|
|||
Ideally, the application itself neither knows nor cares about the runtime environment.
|
||||
|
||||
|
||||
我们的做法正是如此。这里的 `Title` 服务是 Angular*浏览器平台*的一部分。如果在其它平台上引导应用程序,就得提供另一个专为那个平台准备的 `Title` 服务。
|
||||
你的做法正是如此。这里的 `Title` 服务是 Angular*浏览器平台*的一部分。如果在其它平台上引导应用程序,就得提供另一个专为那个平台准备的 `Title` 服务。
|
||||
|
|
|
@ -111,7 +111,7 @@ If a module provides both providers and declarations (components, directives, pi
|
|||
|
||||
To make this more concrete, consider the `RouterModule` as an example. `RouterModule` needs to provide the `Router` service, as well as the `RouterOutlet` directive. `RouterModule` has to be imported by the root application module so that the application has a `Router` and the application has at least one `RouterOutlet`. It also must be imported by the individual route components so that they can place `RouterOutlet` directives into their template for sub-routes.
|
||||
|
||||
我们以 `RouterModule` 为例来具体说说。`RouterModule` 要提供 `Router` 服务,还要提供 `RouterOutlet` 指令。
|
||||
以 `RouterModule` 为例具体说说。`RouterModule` 要提供 `Router` 服务,还要提供 `RouterOutlet` 指令。
|
||||
`RouterModule` 要由根应用模块导入,以便该应用拥有一个路由器,而且它还需要至少一个 `RouterOutlet`。
|
||||
`RouterModule` 还必须由各个独立的路由组件导入,让它们能在自己的模板中使用 `RouterOutlet` 指令来支持其子路由。
|
||||
|
||||
|
@ -187,7 +187,7 @@ Here's `forRoot()` that takes a `UserServiceConfig` object:
|
|||
|
||||
Lastly, call it within the `imports` list of the `AppModule`.
|
||||
|
||||
最后,我们在 `AppModule` 的 `imports`*列表*中调用它。
|
||||
最后,在 `AppModule` 的 `imports`*列表*中调用它。
|
||||
|
||||
<code-example path="ngmodules/src/app/app.module.ts" region="import-for-root" title="src/app/app.module.ts (imports)" linenums="false">
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
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 树,以及我们该如何写自己的结构型指令来完成同样的任务。
|
||||
本章将看看 Angular 如何用*结构型指令*操纵 DOM 树,以及你该如何写自己的结构型指令来完成同样的任务。
|
||||
|
||||
Try the <live-example></live-example>.
|
||||
|
||||
|
@ -57,7 +57,7 @@ Angular desugars this notation into a marked-up `<ng-template>` that surrounds t
|
|||
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)。
|
||||
在这个例子中,你将学到[星号(*)这个简写方法](guide/structural-directives#asterisk),而这个字符串是一个[*微语法*](guide/structural-directives#microsyntax),而不是通常的[模板表达式](guide/template-syntax#template-expressions)。
|
||||
Angular 会解开这个语法糖,变成一个 `<ng-template>` 标记,包裹着宿主元素及其子元素。
|
||||
每个结构型指令都可以用这个模板做点不同的事情。
|
||||
|
||||
|
@ -67,7 +67,7 @@ described in the [_Template Syntax_](guide/template-syntax) guide and seen in sa
|
|||
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 文档的例子中到处都在用它。下面是模板中的例子:
|
||||
你在[*模板语法*](guide/template-syntax)一章中学过它,并且在 Angular 文档的例子中到处都在用它。下面是模板中的例子:
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (built-in)" region="built-in">
|
||||
|
||||
|
@ -89,7 +89,7 @@ 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`。
|
||||
在本章中,你将看到指令同时具有两种拼写形式*大驼峰 `UpperCamelCase` 和小驼峰 `lowerCamelCase`,比如你已经看过的 `NgIf` 和 `ngIf`。
|
||||
这里的原因在于,`NgIf` 引用的是指令的*类名*,而 `ngIf` 引用的是指令的*属性名*。
|
||||
|
||||
A directive _class_ is spelled in _UpperCamelCase_ (`NgIf`).
|
||||
|
@ -126,7 +126,7 @@ changes several element styles at the same time.
|
|||
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)*结构型*指令。
|
||||
你可以在一个宿主元素上应用多个*属性型*指令,但[只能应用一个](guide/structural-directives#one-per-element)*结构型*指令。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -139,7 +139,7 @@ You can [only apply one](guide/structural-directives#one-per-element) _structura
|
|||
`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 树出现或消失。
|
||||
`NgIf` 是一个很好的结构型指令案例:它接受一个布尔值,并据此让一整块 DOM 树出现或消失。
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-true)" region="ngif-true">
|
||||
|
||||
|
@ -195,7 +195,8 @@ 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 元素上,
|
||||
对于简单的段落,隐藏和移除之间的差异影响不大,但对于资源占用较多的组件是不一样的。
|
||||
当隐藏掉一个元素时,组件的行为还在继续 —— 它仍然附加在它所属的 DOM 元素上,
|
||||
它也仍在监听事件。Angular 会继续检查哪些能影响数据绑定的变更。
|
||||
组件原本要做的那些事情仍在继续。
|
||||
|
||||
|
@ -219,14 +220,14 @@ 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` 这样的结构型指令来收回用不到的资源。
|
||||
但是,除非有非常强烈的理由来保留它们,否则你会更倾向于移除用户看不见的那些 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}
|
||||
|
||||
|
@ -311,7 +312,7 @@ 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](guide/structural-directives#microsyntax).
|
||||
|
||||
我们可以通过把一个字符串赋值给 `ngFor` 来启用这些特性,这个字符串使用 Angular 的[微语法](guide/structural-directives#microsyntax)。
|
||||
你可以通过把一个字符串赋值给 `ngFor` 来启用这些特性,这个字符串使用 Angular 的[微语法](guide/structural-directives#microsyntax)。
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
@ -333,7 +334,7 @@ In this example, the `[ngClass]="odd"` stays on the `<div>`.
|
|||
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>`:
|
||||
|
||||
Angular 微语法能让我们通过简短的、友好的字符串来配置一个指令。
|
||||
Angular 微语法能让你通过简短的、友好的字符串来配置一个指令。
|
||||
微语法解析器把这个字符串翻译成 `<ng-template>` 上的属性:
|
||||
|
||||
* The `let` keyword declares a [_template input variable_](guide/structural-directives#template-input-variable)
|
||||
|
@ -341,7 +342,7 @@ that you reference within the template. The input variables in this example are
|
|||
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` 关键字声明一个[模板输入变量](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`),
|
||||
|
@ -414,9 +415,9 @@ 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`)在模板中声明一个模板*输入*变量。
|
||||
你使用 `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.
|
||||
|
@ -440,8 +441,8 @@ Someday you'll want to repeat a block of HTML but only when a particular conditi
|
|||
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 不允许。这是因为我们在一个元素上只能放一个*结构型*指令。
|
||||
有时你会希望只有当特定的条件为真时才重复渲染一个 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?
|
||||
|
@ -487,7 +488,7 @@ It's an _attribute_ directive that controls the behavior of the other two switch
|
|||
That's why you write `[ngSwitch]`, never `*ngSwitch`.
|
||||
|
||||
`NgSwitch` 本身不是结构型指令,而是一个*属性型*指令,它控制其它两个 switch 指令的行为。
|
||||
这也就是为什么我们要写成 `[ngSwitch]` 而不是 `*ngSwitch` 的原因。
|
||||
这也就是为什么你要写成 `[ngSwitch]` 而不是 `*ngSwitch` 的原因。
|
||||
|
||||
`NgSwitchCase` and `NgSwitchDefault` _are_ structural directives.
|
||||
You attach them to elements using the asterisk (*) prefix notation.
|
||||
|
@ -495,7 +496,7 @@ An `NgSwitchCase` displays its host element when its value matches the switch va
|
|||
The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` matches the switch value.
|
||||
|
||||
`NgSwitchCase` 和 `NgSwitchDefault` *都是*结构型指令。
|
||||
因此我们要使用星号(`*`)前缀来把它们附着到元素上。
|
||||
因此你要使用星号(`*`)前缀来把它们附着到元素上。
|
||||
`NgSwitchCase` 会在它的值匹配上选项值的时候显示它的宿主元素。
|
||||
`NgSwitchDefault` 则会当没有兄弟 `NgSwitchCase` 匹配上时显示它的宿主元素。
|
||||
|
||||
|
@ -538,7 +539,7 @@ it's still important to know that Angular creates a `<ng-template>` and to under
|
|||
You'll refer to the `<ng-template>` when you [write your own structural directive](guide/structural-directives#unless).
|
||||
|
||||
虽然很少有理由在模板中使用结构型指令的*属性*形式和*元素*形式,但这些幕后知识仍然是很重要的,即:Angular 会创建 `<ng-template>`,还要了解它的工作原理。
|
||||
当需要[写自己的结构型指令](guide/structural-directives#unless)时,我们就要使用 `<ng-template>`。
|
||||
当需要[写自己的结构型指令](guide/structural-directives#unless)时,你就要使用 `<ng-template>`。
|
||||
|
||||
{@a template}
|
||||
|
||||
|
@ -576,7 +577,7 @@ Angular 抹掉了中间的那个 "Hip!" ,让欢呼声显得不再那么热烈
|
|||
A structural directive puts a `<ng-template>` to work
|
||||
as you'll see when you [write your own structural directive](guide/structural-directives#unless).
|
||||
|
||||
结构型指令会让 `<ng-template>` 正常工作,在我们[写自己的结构型指令](guide/structural-directives#unless)时就会看到这一点。
|
||||
结构型指令会让 `<ng-template>` 正常工作,在你[写自己的结构型指令](guide/structural-directives#unless)时就会看到这一点。
|
||||
|
||||
{@a ngcontainer}
|
||||
|
||||
|
@ -599,7 +600,7 @@ The list element (`<li>`) is a typical host element of an `NgFor` repeater.
|
|||
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.
|
||||
|
||||
当没有这样一个单一的宿主元素时,我们可以把这些内容包裹在一个原生的 HTML 容器元素中,比如 `<div>`,并且把结构型指令附加到这个"包裹"上。
|
||||
当没有这样一个单一的宿主元素时,你就可以把这些内容包裹在一个原生的 HTML 容器元素中,比如 `<div>`,并且把结构型指令附加到这个"包裹"上。
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif)" region="ngif">
|
||||
|
||||
|
@ -624,7 +625,7 @@ For example, suppose you have the following paragraph layout.
|
|||
|
||||
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
|
||||
|
||||
而我们的 CSS 样式规则是应用于 `<p>` 元素下的 `<span>` 的。
|
||||
而你的 CSS 样式规则是应用于 `<p>` 元素下的 `<span>` 的。
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.css" linenums="false" title="src/app/app.component.css (p-span)" region="p-span">
|
||||
|
||||
|
@ -647,7 +648,7 @@ For example, the `<select>` element requires `<option>` children.
|
|||
You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
|
||||
|
||||
另一个问题是:有些 HTML 元素需要所有的直属下级都具有特定的类型。
|
||||
比如,`<select>` 元素要求直属下级必须为 `<option>`,那么我们就没办法把这些选项包装进 `<div>` 或 `<span>` 中。
|
||||
比如,`<select>` 元素要求直属下级必须为 `<option>`,那就没办法把这些选项包装进 `<div>` 或 `<span>` 中。
|
||||
|
||||
When you try this,
|
||||
|
||||
|
@ -680,7 +681,7 @@ Angular 的 `<ng-container>` 是一个分组元素,但它不会污染样式或
|
|||
|
||||
Here's the conditional paragraph again, this time using `<ng-container>`.
|
||||
|
||||
下面是重新实现的条件化段落,这次我们使用 `<ng-container>`。
|
||||
下面是重新实现的条件化段落,这次使用 `<ng-container>`。
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
|
||||
|
||||
|
@ -696,7 +697,7 @@ It renders properly.
|
|||
|
||||
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
|
||||
|
||||
我们再用 `<ng-container>` 来根据条件排除选择框中的某个 `<option>`。
|
||||
现在用 `<ng-container>` 来根据条件排除选择框中的某个 `<option>`。
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer">
|
||||
|
||||
|
@ -745,7 +746,7 @@ 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***.
|
||||
|
||||
在本节中,我们会写一个名叫 `UnlessDirective` 的结构型指令,它是 `NgIf` 的反义词。
|
||||
在本节中,你会写一个名叫 `UnlessDirective` 的结构型指令,它是 `NgIf` 的反义词。
|
||||
`NgIf` 在条件为 `true` 的时候显示模板内容,而 `UnlessDirective` 则会在条件为 `false` 时显示模板内容。
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (appUnless-1)" region="appUnless-1">
|
||||
|
@ -762,7 +763,7 @@ Creating a directive is similar to creating a component.
|
|||
|
||||
* Import the `Input`, `TemplateRef`, and `ViewContainerRef` symbols; you'll need them for _any_ structural directive.
|
||||
|
||||
导入符号 `Input`、`TemplateRef` 和 `ViewContainerRef`,我们在*任何*结构型指令中都会需要它们。
|
||||
导入符号 `Input`、`TemplateRef` 和 `ViewContainerRef`,你在*任何*结构型指令中都会需要它们。
|
||||
|
||||
* Apply the decorator to the directive class.
|
||||
|
||||
|
@ -820,11 +821,11 @@ You'll acquire the `<ng-template>` contents with a
|
|||
and access the _view container_ through a
|
||||
[`ViewContainerRef`](api/core/ViewContainerRef "API: ViewContainerRef").
|
||||
|
||||
我们可以使用[`TemplateRef`](api/core/TemplateRef "API: TemplateRef")取得 `<ng-template>` 的内容,并通过[`ViewContainerRef`](api/core/ViewContainerRef "API: ViewContainerRef")来访问这个*视图容器*。
|
||||
你可以使用[`TemplateRef`](api/core/TemplateRef "API: TemplateRef")取得 `<ng-template>` 的内容,并通过[`ViewContainerRef`](api/core/ViewContainerRef "API: ViewContainerRef")来访问这个*视图容器*。
|
||||
|
||||
You inject both in the directive constructor as private variables of the class.
|
||||
|
||||
我们可以把它们都注入到指令的构造函数中,作为该类的私有属性。
|
||||
你可以把它们都注入到指令的构造函数中,作为该类的私有属性。
|
||||
|
||||
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" title="src/app/unless.directive.ts (ctor)" region="ctor">
|
||||
|
||||
|
@ -855,7 +856,7 @@ Read about `@Input` in the [_Template Syntax_](guide/template-syntax#inputs-outp
|
|||
Angular sets the `appUnless` property whenever the value of the condition changes.
|
||||
Because the `appUnless` property does work, it needs a setter.
|
||||
|
||||
一旦该值的条件发生了变化,Angular 就会去设置 `appUnless` 属性,这时候,我们就需要为它定义一个设置器(setter)。
|
||||
一旦该值的条件发生了变化,Angular 就会去设置 `appUnless` 属性。因为不能用 `appUnless` 属性,所以你要为它定义一个设置器(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.
|
||||
|
@ -949,7 +950,7 @@ Here is the source from the `src/app/` folder.
|
|||
|
||||
You learned
|
||||
|
||||
我们学到了
|
||||
你学到了
|
||||
|
||||
* that structural directives manipulate HTML layout.
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ If you fully understand the meaning behind the guideline and have a good reason
|
|||
|
||||
**Avoid** indicates something you should almost never do. Code examples to *avoid* have an unmistakeable red header.
|
||||
|
||||
**避免**标志着我们决不应该做的事。需要*避免*的代码范例会有明显的红色标题。
|
||||
**避免**标志着你决不应该做的事。需要*避免*的代码范例会有明显的红色标题。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -305,7 +305,7 @@ Naming conventions are hugely important to maintainability and readability. This
|
|||
|
||||
**Why?** Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency.
|
||||
|
||||
**为何?**命名约定提供了一致的方式来查找内容,让我们一眼就能锁定。
|
||||
**为何?**命名约定提供了一致的方式来查找内容,让你一眼就能找到。
|
||||
项目的一致性是至关重要的。团队内的一致性也很重要。整个公司的一致性会提供惊人的效率。
|
||||
|
||||
</div>
|
||||
|
@ -314,7 +314,7 @@ Naming conventions are hugely important to maintainability and readability. This
|
|||
|
||||
**Why?** The naming conventions should simply help find desired code faster and make it easier to understand.
|
||||
|
||||
**为何?**命名约定帮助我们更快得找到不在手头的代码,更容易理解它。
|
||||
**为何?**命名约定帮助你更快得找到想找的代码,也更容易理解它。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1712,7 +1712,7 @@ By convention, upper camel case indicates a constructable asset.
|
|||
**Why?** TypeScript helps enforce that intent by requiring immediate initialization and by
|
||||
preventing subsequent re-assignment.
|
||||
|
||||
**为何?** TypeScript 会要求在声明时立即初始化,并阻止再次赋值,以确保达成我们的意图。
|
||||
**为何?** TypeScript 会要求在声明时立即初始化,并阻止再次赋值,以帮助确保你的设计意图。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -2004,7 +2004,7 @@ Use the naming conventions for files in this guide.
|
|||
|
||||
所有内容都遵循每个文件一个特性的原则。每个组件、服务和管道都在自己的文件里。
|
||||
所有第三方程序包保存到其它目录里,而不是 `src` 目录。
|
||||
你不会修改它们,所以不希望它们弄乱我们的应用程序。
|
||||
你不会修改它们,所以不希望它们弄乱你的应用程序。
|
||||
使用本指南介绍的文件命名约定。
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
@ -2045,7 +2045,7 @@ To confirm your intuition about a particular structure, ask:
|
|||
_can I quickly open and start work in all of the related files for this feature_?
|
||||
|
||||
**为何?**LIFT 提供了一致的结构,它具有扩展性强、模块化的特性。因为容易快速锁定代码,提高了开发者的效率。
|
||||
另外,检查应用结构是否合理的方法是问问自己:我们能快速打开与此特性有关的所有文件并开始工作吗?
|
||||
另外,检查应用结构是否合理的方法是问问自己:我能快速打开与此特性有关的所有文件并开始工作吗?
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ This page is a comprehensive technical reference to the Angular template languag
|
|||
It explains basic principles of the template language and describes most of the syntax that you'll encounter elsewhere in the documentation.
|
||||
|
||||
这是一篇关于 Angular 模板语言的技术大全。
|
||||
它解释了模板语言的基本原理,并描述了我们将在文档中其它地方遇到的大部分语法。
|
||||
它解释了模板语言的基本原理,并描述了你将在文档中其它地方遇到的大部分语法。
|
||||
|
||||
Many code snippets illustrate the points and concepts, all of them available
|
||||
in the <live-example title="Template Syntax Live Code"></live-example>.
|
||||
|
@ -60,7 +60,7 @@ In the following sections, you'll learn how to get and set DOM (Document Object
|
|||
|
||||
Begin with the first form of data binding—interpolation—to see how much richer template HTML can be.
|
||||
|
||||
我们首先看看数据绑定的第一种形式 —— 插值表达式,它展示了模板的 HTML 可以有多丰富。
|
||||
首先看看数据绑定的第一种形式 —— 插值表达式,它展示了模板的 HTML 可以有多丰富。
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -72,7 +72,7 @@ Begin with the first form of data binding—interpolation—to see how m
|
|||
|
||||
You met the double-curly braces of interpolation, `{{` and `}}`, early in your Angular education.
|
||||
|
||||
在以前的 Angular 教程中,我们遇到过由双花括号括起来的插值表达式,`{{` 和 `}}`。
|
||||
在以前的 Angular 教程中,你遇到过由双花括号括起来的插值表达式,`{{` 和 `}}`。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="first-interpolation" title="src/app/app.component.html" linenums="false">
|
||||
|
||||
|
@ -123,8 +123,8 @@ It's convenient to think so, and you rarely suffer for this mistake.
|
|||
Though this is not exactly true. Interpolation is a special syntax that Angular converts into a
|
||||
[property binding](guide/template-syntax#property-binding), as is explained [below](guide/template-syntax#property-binding-or-interpolation).
|
||||
|
||||
表面上看,我们在元素标签之间插入了结果和对标签的属性进行了赋值。
|
||||
这样思考起来很方便,并且这个误解很少给我们带来麻烦。
|
||||
表面上看,你在元素标签之间插入了结果和对标签的属性进行了赋值。
|
||||
这样思考起来很方便,并且这个误解很少给你带来麻烦。
|
||||
但严格来讲,这是不对的。插值表达式是一个特殊的语法,Angular 把它转换成了[属性绑定](guide/template-syntax#property-binding),[后面](guide/template-syntax#property-binding-or-interpolation)将会解释这一点。
|
||||
|
||||
But first, let's take a closer look at template expressions and statements.
|
||||
|
@ -227,7 +227,7 @@ the template variable name takes precedence, followed by a name in the directive
|
|||
and, lastly, the component's member names.
|
||||
|
||||
表达式中的上下文变量是由*模板变量*、指令的*上下文变量*(如果有)和组件的*成员*叠加而成的。
|
||||
如果我们要引用的变量名存在于一个以上的命名空间中,那么,模板变量是最优先的,其次是指令的上下文变量,最后是组件的成员。
|
||||
如果你要引用的变量名存在于一个以上的命名空间中,那么,模板变量是最优先的,其次是指令的上下文变量,最后是组件的成员。
|
||||
|
||||
The previous example presents such a name collision. The component has a `hero`
|
||||
property and the `*ngFor` defines a `hero` template variable.
|
||||
|
@ -303,7 +303,7 @@ Angular executes template expressions after every change detection cycle.
|
|||
Change detection cycles are triggered by many asynchronous activities such as
|
||||
promise resolutions, http results, timer events, keypresses and mouse moves.
|
||||
|
||||
Angular 执行模板表达式比我们想象的频繁。
|
||||
Angular 会在每个变更检测周期后执行模板表达式。
|
||||
它们可能在每一次按键或鼠标移动后被调用。
|
||||
|
||||
Expressions should finish quickly or the user experience may drag, especially on slower devices.
|
||||
|
@ -376,7 +376,7 @@ That's the whole point of an event.
|
|||
It's how you update application state from user action.
|
||||
|
||||
模板语句*有副作用*。
|
||||
这是事件处理的关键。因为我们要根据用户的输入更新应用状态。
|
||||
这是事件处理的关键。因为你要根据用户的输入更新应用状态。
|
||||
|
||||
Responding to events is the other side of Angular's "unidirectional data flow".
|
||||
You're free to change anything, anywhere, during this turn of the event loop.
|
||||
|
@ -490,16 +490,16 @@ the application is easier to write, read, and maintain if you turn these chores
|
|||
You simply declare bindings between binding sources and target HTML elements and let the framework do the work.
|
||||
|
||||
数据绑定是一种机制,用来协调用户所见和应用数据。
|
||||
虽然我们能往 HTML 推送值或者从 HTML 拉取值,
|
||||
但如果把这些琐事交给数据绑定框架处理,
|
||||
应用会更容易编写、阅读和维护。
|
||||
只要简单地在绑定源和目标 HTML 元素之间声明绑定,框架就会完成这项工作。
|
||||
虽然你能往 HTML 推送值或者从 HTML 拉取值,
|
||||
但如果把这些琐事交给数据绑定框架处理,
|
||||
应用会更容易编写、阅读和维护。
|
||||
只要简单地在绑定源和目标 HTML 元素之间声明绑定,框架就会完成这项工作。
|
||||
|
||||
Angular provides many kinds of data binding.
|
||||
This guide covers most of them, after a high-level view of Angular data binding and its syntax.
|
||||
|
||||
Angular 提供了各种各样的数据绑定,本章将逐一讨论。
|
||||
不过我们要先从高层视角来看看 Angular 数据绑定及其语法。
|
||||
先从高层视角来看看 Angular 数据绑定及其语法。
|
||||
|
||||
Binding types can be grouped into three categories distinguished by the direction of data flow:
|
||||
from the _source-to-view_, from _view-to-source_, and in the two-way sequence: _view-to-source-to-view_:
|
||||
|
@ -676,7 +676,7 @@ The target name is the name of a _property_. It may look like the name of an _at
|
|||
To appreciate the difference, you must develop a new way to think about template HTML.
|
||||
|
||||
这个目标名就是*属性(Property)*的名字。它可能看起来像是*元素属性(Attribute)*的名字,但它不是。
|
||||
要理解它们的不同点,我们必须尝试用另一种方式来审视模板中的 HTML。
|
||||
要理解它们的不同点,你必须尝试用另一种方式来审视模板中的 HTML。
|
||||
|
||||
### A new mental model
|
||||
|
||||
|
@ -685,20 +685,20 @@ To appreciate the difference, you must develop a new way to think about template
|
|||
With all the power of data binding and the ability to extend the HTML vocabulary
|
||||
with custom markup, it is tempting to think of template HTML as *HTML Plus*.
|
||||
|
||||
数据绑定的威力和允许用自定义标记扩展 HTML 词汇的能力,容易误导我们把模板 HTML 当成 *HTML+*。
|
||||
数据绑定的威力和允许用自定义标记扩展 HTML 词汇的能力,会让你把模板 HTML 当成 *HTML+*。
|
||||
|
||||
It really *is* HTML Plus.
|
||||
But it's also significantly different than the HTML you're used to.
|
||||
It requires a new mental model.
|
||||
|
||||
它其实*就是* HTML+。
|
||||
但它也跟我们熟悉的 HTML 有着显著的不同。
|
||||
我们需要一种新的思维模型。
|
||||
但它也跟你曾使用的 HTML 有着显著的不同。
|
||||
这里需要一种新的思维模型。
|
||||
|
||||
In the normal course of HTML development, you create a visual structure with HTML elements, and
|
||||
you modify those elements by setting element attributes with string constants.
|
||||
|
||||
在正常的 HTML 开发过程中,我们使用 HTML 元素创建视觉结构,
|
||||
在正常的 HTML 开发过程中,你使用 HTML 元素来创建视觉结构,
|
||||
通过把字符串常量设置到元素的 attribute 来修改那些元素。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="img+button" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -707,7 +707,7 @@ you modify those elements by setting element attributes with string constants.
|
|||
|
||||
You still create a structure and initialize attribute values this way in Angular templates.
|
||||
|
||||
在 Angular 模板中,我们仍使用同样的方式来创建结构和初始化 attribute 值。
|
||||
在 Angular 模板中,你仍使用同样的方式创建结构和初始化 attribute 值。
|
||||
|
||||
Then you learn to create new elements with components that encapsulate HTML
|
||||
and drop them into templates as if they were native HTML elements.
|
||||
|
@ -724,7 +724,7 @@ That's HTML Plus.
|
|||
|
||||
Then you learn about data binding. The first binding you meet might look like this:
|
||||
|
||||
现在开始学习数据绑定。我们碰到的第一种数据绑定是这样的:
|
||||
现在开始学习数据绑定。你碰到的第一种数据绑定是这样的:
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="disabled-button-1" title="src/app/app.component.html" linenums="false">
|
||||
|
||||
|
@ -734,14 +734,14 @@ You'll get to that peculiar bracket notation in a moment. Looking beyond it,
|
|||
your intuition suggests that you're binding to the button's `disabled` attribute and setting
|
||||
it to the current value of the component's `isUnchanged` property.
|
||||
|
||||
过会儿再认识那个怪异的方括号记法。直觉告诉我们,我们正在绑定按钮的 `disabled` attribute。
|
||||
过会儿再认识那个怪异的方括号记法。直觉告诉你,你正在绑定按钮的 `disabled` attribute。
|
||||
并把它设置为组件的 `isUnchanged` 属性的当前值。
|
||||
|
||||
Your intuition is incorrect! Your everyday HTML mental model is misleading.
|
||||
In fact, once you start data binding, you are no longer working with HTML *attributes*. You aren't setting attributes.
|
||||
You are setting the *properties* of DOM elements, components, and directives.
|
||||
|
||||
但我们的直觉是错的!日常的 HTML 思维模式在误导我们。
|
||||
但你的直觉是错的!日常的 HTML 思维模式在误导着你。
|
||||
实际上,一旦开始数据绑定,就不再跟 HTML attribute 打交道了。
|
||||
这里不是设置 attribute,而是设置 DOM 元素、组件和指令的 property。
|
||||
|
||||
|
@ -773,11 +773,11 @@ The distinction between an HTML attribute and a DOM property is crucial to under
|
|||
|
||||
* Many HTML attributes appear to map to properties ... but not in the way you might think!
|
||||
|
||||
大量 HTML attribute 看起来映射到了 property…… 但却不像我们想的那样!
|
||||
大量 HTML attribute 看起来映射到了 property…… 但却不像你想的那样!
|
||||
|
||||
That last category is confusing until you grasp this general rule:
|
||||
|
||||
最后一类尤其让人困惑…… 除非我们能理解这个普遍原则:
|
||||
最后一类尤其让人困惑…… 除非你能理解这个普遍原则:
|
||||
|
||||
**Attributes *initialize* DOM properties and then they are done.
|
||||
Property values can change; attribute values can't.**
|
||||
|
@ -795,7 +795,7 @@ But the HTML `value` *attribute* remains unchanged as you discover if you ask th
|
|||
about that attribute: `input.getAttribute('value')` returns "Bob".
|
||||
|
||||
当用户在输入框中输入 “Sally” 时,DOM 元素的 `value` 这个 *property* 变成了 “Sally”。
|
||||
但是该 HTML 的 `value` 这个 *attribute* 保持不变。如果我们读取 input 元素的 attribute,就会发现确实没变:
|
||||
但是该 HTML 的 `value` 这个 *attribute* 保持不变。如果你读取 input 元素的 attribute,就会发现确实没变:
|
||||
`input.getAttribute('value') // 返回 "Bob"`。
|
||||
|
||||
The HTML attribute `value` specifies the *initial* value; the DOM `value` property is the *current* value.
|
||||
|
@ -808,12 +808,12 @@ When you add the `disabled` *attribute*, its presence alone initializes the but
|
|||
so the button is disabled.
|
||||
|
||||
`disabled` 这个 attribute 是另一种特例。按钮的 `disabled` 这个 *property* 是 `false`,因为默认情况下按钮是可用的。
|
||||
当我们添加 `disabled` 这个 *attribute* 时,只要它出现了按钮的 `disabled` 这个 *property* 就初始化为 `true`,于是按钮就被禁用了。
|
||||
当你添加 `disabled` 这个 *attribute* 时,只要它出现了按钮的 `disabled` 这个 *property* 就初始化为 `true`,于是按钮就被禁用了。
|
||||
|
||||
Adding and removing the `disabled` *attribute* disables and enables the button. The value of the *attribute* is irrelevant,
|
||||
which is why you cannot enable a button by writing `<button disabled="false">Still Disabled</button>`.
|
||||
|
||||
添加或删除 `disabled` 这个 *attribute* 会禁用或启用这个按钮。但 *attribute* 的值无关紧要,这就是我们为什么没法通过
|
||||
添加或删除 `disabled` 这个 *attribute* 会禁用或启用这个按钮。但 *attribute* 的值无关紧要,这就是你为什么没法通过
|
||||
`<button disabled="false">仍被禁用</button>` 这种写法来启用按钮。
|
||||
|
||||
Setting the button's `disabled` *property* (say, with an Angular binding) disables or enables the button.
|
||||
|
@ -1082,7 +1082,7 @@ The following table summarizes:
|
|||
|
||||
With this broad view in mind, you're ready to look at binding types in detail.
|
||||
|
||||
放开眼界,我们来看看每种绑定类型的具体情况。
|
||||
放开眼界,来看看每种绑定类型的具体情况。
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -1236,15 +1236,15 @@ You can't assign a value to anything in a property binding expression nor use th
|
|||
Of course, the expression might invoke a property or method that has side effects.
|
||||
Angular has no way of knowing that or stopping you.
|
||||
|
||||
当然,表达式可能会调用具有副作用的属性或方法。但 Angular 没法知道这一点,也没法阻止我们。
|
||||
当然,表达式可能会调用具有副作用的属性或方法。但 Angular 没法知道这一点,也没法阻止你。
|
||||
|
||||
The expression could call something like `getFoo()`. Only you know what `getFoo()` does.
|
||||
If `getFoo()` changes something and you happen to be binding to that something, you risk an unpleasant experience.
|
||||
Angular may or may not display the changed value. Angular may detect the change and throw a warning error.
|
||||
In general, stick to data properties and to methods that return values and do no more.
|
||||
|
||||
表达式中可以调用像 `getFoo()` 这样的方法。只有我们知道 `getFoo()` 干了什么。
|
||||
如果 `getFoo()` 改变了某个东西,恰好又绑定到个这个东西,我们就可能把自己坑了。
|
||||
表达式中可以调用像 `getFoo()` 这样的方法。只有你知道 `getFoo()` 干了什么。
|
||||
如果 `getFoo()` 改变了某个东西,恰好又绑定到个这个东西,你就可能把自己坑了。
|
||||
Angular 可能显示也可能不显示变化后的值。Angular 还可能检测到变化,并抛出警告型错误。
|
||||
一般建议是,只绑定数据属性和那些只返回值而不做其它事情的方法。
|
||||
|
||||
|
@ -1318,7 +1318,7 @@ just as well for directive and component property initialization.
|
|||
The following example initializes the `prefix` property of the `HeroDetailComponent` to a fixed string,
|
||||
not a template expression. Angular sets it and forgets about it.
|
||||
|
||||
我们经常这样在标准 HTML 中用这种方式初始化 attribute,这种方式也可以用在初始化指令和组件的属性。
|
||||
你经常这样在标准 HTML 中用这种方式初始化 attribute,这种方式也可以用在初始化指令和组件的属性。
|
||||
下面这个例子把 `HeroDetailComponent` 的 `prefix` 属性初始化为固定的字符串,而不是模板表达式。Angular 设置它,然后忘记它。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-7" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -1338,7 +1338,7 @@ The `[hero]` binding, on the other hand, remains a live binding to the component
|
|||
You often have a choice between interpolation and property binding.
|
||||
The following binding pairs do the same thing:
|
||||
|
||||
我们通常得在插值表达式和属性绑定之间做出选择。
|
||||
你通常得在插值表达式和属性绑定之间做出选择。
|
||||
下列这几对绑定做的事情完全相同:
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-vs-interpolation" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -1356,7 +1356,7 @@ You suggest establishing coding style rules and choosing the form that
|
|||
both conforms to the rules and feels most natural for the task at hand.
|
||||
|
||||
当要渲染的数据类型是字符串时,没有技术上的理由证明哪种形式更好。
|
||||
我们倾向于可读性,所以倾向于插值表达式。
|
||||
你倾向于可读性,所以倾向于插值表达式。
|
||||
建议建立代码风格规则,选择一种形式,
|
||||
这样,既遵循了规则,又能让手头的任务做起来更自然。
|
||||
|
||||
|
@ -1448,7 +1448,7 @@ There are no property targets to bind to.
|
|||
|
||||
This fact becomes painfully obvious when you write something like this.
|
||||
|
||||
如果想写出类似下面这样的东西,现状会令我们痛苦:
|
||||
如果想写出类似下面这样的东西,就会暴露出痛点了:
|
||||
|
||||
<code-example language="html">
|
||||
|
||||
|
@ -1476,7 +1476,7 @@ interpolation and property binding can set only *properties*, not attributes.
|
|||
|
||||
You need attribute bindings to create and bind to such attributes.
|
||||
|
||||
我们需要 attribute 绑定来创建和绑定到这样的 attribute。
|
||||
你需要 attribute 绑定来创建和绑定到这样的 attribute。
|
||||
|
||||
Attribute binding syntax resembles property binding.
|
||||
Instead of an element property between brackets, start with the prefix **`attr`**,
|
||||
|
@ -1592,7 +1592,7 @@ It removes the class when the expression is falsy.
|
|||
While this is a fine way to toggle a single class name,
|
||||
the [NgClass directive](guide/template-syntax#ngClass) is usually preferred when managing multiple class names at the same time.
|
||||
|
||||
虽然这是切换单一类名的好办法,但我们通常更喜欢使用 [NgClass 指令](guide/template-syntax#ngClass) 来同时管理多个类名。
|
||||
虽然这是切换单一类名的好办法,但人们通常更喜欢使用 [NgClass 指令](guide/template-syntax#ngClass) 来同时管理多个类名。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1632,7 +1632,7 @@ The following example conditionally sets the font size in “em” and “%”
|
|||
While this is a fine way to set a single style,
|
||||
the [NgStyle directive](guide/template-syntax#ngStyle) is generally preferred when setting several inline styles at the same time.
|
||||
|
||||
虽然这是设置单一样式的好办法,但我们通常更喜欢使用 [NgStyle 指令](guide/template-syntax#ngStyle) 来同时设置多个内联样式。
|
||||
虽然这是设置单一样式的好办法,但人们通常更喜欢使用 [NgStyle 指令](guide/template-syntax#ngStyle) 来同时设置多个内联样式。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1871,7 +1871,7 @@ These changes percolate through the system and are ultimately displayed in this
|
|||
|
||||
You often want to both display a data property and update that property when the user makes changes.
|
||||
|
||||
我们经常需要显示数据属性,并在用户作出更改时更新该属性。
|
||||
你经常需要显示数据属性,并在用户作出更改时更新该属性。
|
||||
|
||||
On the element side that takes a combination of setting a specific element property
|
||||
and listening for an element change event.
|
||||
|
@ -1957,7 +1957,7 @@ Clearly the two-way binding syntax is a great convenience compared to separate p
|
|||
It would be convenient to use two-way binding with HTML form elements like `<input>` and `<select>`.
|
||||
However, no native HTML element follows the `x` value and `xChange` event pattern.
|
||||
|
||||
我们希望能在像 `<input>` 和 `<select>` 这样的 HTML 元素上使用双向数据绑定。
|
||||
如果能在像 `<input>` 和 `<select>` 这样的 HTML 元素上使用双向数据绑定就更好了。
|
||||
可惜,原生 HTML 元素不遵循 `x` 值和 `xChange` 事件的模式。
|
||||
|
||||
Fortunately, the Angular [_NgModel_](guide/template-syntax#ngModel) directive is a bridge that enables two-way binding to form elements.
|
||||
|
@ -1995,9 +1995,9 @@ You still benefit from directives that simplify complex tasks.
|
|||
Angular still ships with built-in directives; just not as many.
|
||||
You'll write your own directives, just not as many.
|
||||
|
||||
我们仍然可以从简化复杂任务的指令中获益。
|
||||
你仍然可以从简化复杂任务的指令中获益。
|
||||
Angular 发布时仍然带有内置指令,只是没那么多了。
|
||||
我们仍会写自己的指令,只是没那么多了。
|
||||
你仍会写自己的指令,只是没那么多了。
|
||||
|
||||
This segment reviews some of the most frequently used built-in directives,
|
||||
classified as either [_attribute_ directives](guide/template-syntax#attribute-directives) or [_structural_ directives](guide/template-syntax#structural-directives).
|
||||
|
@ -2050,7 +2050,7 @@ You typically control how elements appear
|
|||
by adding and removing CSS classes dynamically.
|
||||
You can bind to the `ngClass` to add or remove several classes simultaneously.
|
||||
|
||||
我们经常用动态添加或删除 CSS 类的方式来控制元素如何显示。
|
||||
你经常用动态添加或删除 CSS 类的方式来控制元素如何显示。
|
||||
通过绑定到 `NgClass`,可以同时添加或移除多个类。
|
||||
|
||||
A [class binding](guide/template-syntax#class-binding) is a good way to add or remove a *single* class.
|
||||
|
@ -2106,7 +2106,7 @@ It's up to you to call `setCurrentClasses()`, both initially and when the depend
|
|||
You can set inline styles dynamically, based on the state of the component.
|
||||
With `NgStyle` you can set many inline styles simultaneously.
|
||||
|
||||
我们可以根据组件的状态动态设置内联样式。
|
||||
你可以根据组件的状态动态设置内联样式。
|
||||
`NgStyle` 绑定可以同时设置多个内联样式。
|
||||
|
||||
A [style binding](guide/template-syntax#style-binding) is an easy way to set a *single* style value.
|
||||
|
@ -2163,7 +2163,7 @@ It's up to you to call `setCurrentStyles()`, both initially and when the depende
|
|||
When developing data entry forms, you often both display a data property and
|
||||
update that property when the user makes changes.
|
||||
|
||||
当开发数据输入表单时,我们通常都要既显示数据属性又根据用户的更改去修改那个属性。
|
||||
当开发数据输入表单时,你通常都要既显示数据属性又根据用户的更改去修改那个属性。
|
||||
|
||||
Two-way data binding with the `NgModel` directive makes that easy. Here's an example:
|
||||
|
||||
|
@ -2182,7 +2182,7 @@ you must import the `FormsModule` and add it to the NgModule's `imports` list.
|
|||
Learn more about the `FormsModule` and `ngModel` in the
|
||||
[Forms](guide/forms#ngModel) guide.
|
||||
|
||||
在使用 `ngModel` 指令进行双向数据绑定之前,我们必须导入 `FormsModule` 并把它添加到 Angular 模块的 `imports` 列表中。
|
||||
在使用 `ngModel` 指令进行双向数据绑定之前,你必须导入 `FormsModule` 并把它添加到 Angular 模块的 `imports` 列表中。
|
||||
要了解 `FormsModule` 和 `ngModel` 的更多知识,参见[表单](guide/forms#ngModel)一章。
|
||||
|
||||
Here's how to import the `FormsModule` to make `[(ngModel)]` available.
|
||||
|
@ -2245,27 +2245,27 @@ You can't apply `[(ngModel)]` to a non-form native element or a third-party cust
|
|||
until you write a suitable *value accessor*,
|
||||
a technique that is beyond the scope of this guide.
|
||||
|
||||
我们不能把 `[(ngModel)]` 用到非表单类的原生元素或第三方自定义组件上,除非写一个合适的*值访问器*,这种技巧超出了本章的范围。
|
||||
你不能把 `[(ngModel)]` 用到非表单类的原生元素或第三方自定义组件上,除非写一个合适的*值访问器*,这种技巧超出了本章的范围。
|
||||
|
||||
You don't need a _value accessor_ for an Angular component that you write because you
|
||||
can name the value and event properties
|
||||
to suit Angular's basic [two-way binding syntax](guide/template-syntax#two-way) and skip `NgModel` altogether.
|
||||
The [`sizer` shown above](guide/template-syntax#two-way) is an example of this technique.
|
||||
|
||||
我们自己写的 Angular 组件不需要*值访问器*,因为我们可以让值和事件的属性名适应 Angular 基本的[双向绑定语法](guide/template-syntax#two-way),而不使用 `NgModel`。
|
||||
你自己写的 Angular 组件不需要*值访问器*,因为你可以让值和事件的属性名适应 Angular 基本的[双向绑定语法](guide/template-syntax#two-way),而不使用 `NgModel`。
|
||||
[前面看过的 `sizer`](guide/template-syntax#two-way)就是使用这种技巧的例子。
|
||||
|
||||
</div>
|
||||
|
||||
Separate `ngModel` bindings is an improvement over binding to the element's native properties. You can do better.
|
||||
|
||||
使用独立的 `ngModel` 绑定优于绑定到该元素的原生属性,那样我们可以做得更好。
|
||||
使用独立的 `ngModel` 绑定优于绑定到该元素的原生属性,你可以做得更好。
|
||||
|
||||
You shouldn't have to mention the data property twice. Angular should be able to capture
|
||||
the component's data property and set it
|
||||
with a single declaration, which it can with the `[(ngModel)]` syntax:
|
||||
|
||||
我们不用被迫两次引用这个数据属性,Angular 可以捕获该元素的数据属性,并且通过一个简单的声明来设置它,这样它就可以使用 `[(ngModel)]` 语法了。
|
||||
你不用被迫两次引用这个数据属性,Angular 可以捕获该元素的数据属性,并且通过一个简单的声明来设置它,这样它就可以使用 `[(ngModel)]` 语法了。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="NgModel-1" title="src/app/app.component.html" linenums="false">
|
||||
|
||||
|
@ -2316,7 +2316,7 @@ The deep details of structural directives are covered in the
|
|||
[_Structural Directives_](guide/structural-directives) guide
|
||||
where you'll learn:
|
||||
|
||||
关于结构型指令的详情参见[*结构型指令*](guide/structural-directives)一章,在那里我们将学到:
|
||||
关于结构型指令的详情参见[*结构型指令*](guide/structural-directives)一章,在那里你将学到:
|
||||
|
||||
* why you
|
||||
[_prefix the directive name with an asterisk_ (\*)](guide/structural-directives#asterisk "The * in *ngIf").
|
||||
|
@ -2334,7 +2334,7 @@ to group elements when there is no suitable host element for the directive.
|
|||
|
||||
* that you can only apply [one structural directive](guide/structural-directives#one-per-element "one per host element") to an element.
|
||||
|
||||
我们只能往一个元素上应用[一个结构型指令](guide/structural-directives#one-per-element "one per host element")。
|
||||
你只能往一个元素上应用[一个结构型指令](guide/structural-directives#one-per-element "one per host element")。
|
||||
|
||||
_This_ section is an introduction to the common structural directives:
|
||||
|
||||
|
@ -2362,7 +2362,7 @@ You can add or remove an element from the DOM by applying an `NgIf` directive to
|
|||
that element (called the _host element_).
|
||||
Bind the directive to a condition expression like `isActive` in this example.
|
||||
|
||||
通过把 `NgIf` 指令应用到元素上(称为*宿主元素*),我们可以往 DOM 中添加或从 DOM 中移除这个元素。
|
||||
通过把 `NgIf` 指令应用到元素上(称为*宿主元素*),你可以往 DOM 中添加或从 DOM 中移除这个元素。
|
||||
在下面的例子中,该指令绑定到了类似于 `isActive` 这样的条件表达式。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="NgIf-1" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -2390,7 +2390,7 @@ from the DOM, destroying that component and all of its sub-components.
|
|||
You can control the visibility of an element with a
|
||||
[class](guide/template-syntax#class-binding) or [style](guide/template-syntax#style-binding) binding:
|
||||
|
||||
我们也可以通过[类绑定](guide/template-syntax#class-binding)或[样式绑定](guide/template-syntax#style-binding)来显示或隐藏一个元素。
|
||||
你也可以通过[类绑定](guide/template-syntax#class-binding)或[样式绑定](guide/template-syntax#style-binding)来显示或隐藏一个元素。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="NgIf-3" title="src/app/app.component.html" linenums="false">
|
||||
|
||||
|
@ -2467,7 +2467,7 @@ You define a block of HTML that defines how a single item should be displayed.
|
|||
You tell Angular to use that block as a template for rendering each item in the list.
|
||||
|
||||
`NgFor` 是一个_重复器_指令 —— 自定义数据显示的一种方式。
|
||||
我们的目标是展示一个由多个条目组成的列表。首先定义了一个 HTML 块,它规定了单个条目应该如何显示。
|
||||
你的目标是展示一个由多个条目组成的列表。首先定义了一个 HTML 块,它规定了单个条目应该如何显示。
|
||||
再告诉 Angular 把这个块当做模板,渲染列表中的每个条目。
|
||||
|
||||
Here is an example of `NgForOf` applied to a simple `<div>`:
|
||||
|
@ -2546,7 +2546,7 @@ You reference the `hero` input variable within the `NgForOf` host element
|
|||
Here it is referenced first in an interpolation
|
||||
and then passed in a binding to the `hero` property of the `<hero-detail>` component.
|
||||
|
||||
我们可以在 `ngFor` 的宿主元素(及其子元素)中引用模板输入变量 `hero`,从而访问该英雄的属性。
|
||||
你可以在 `ngFor` 的宿主元素(及其子元素)中引用模板输入变量 `hero`,从而访问该英雄的属性。
|
||||
这里它首先在一个插值表达式中被引用到,然后通过一个绑定把它传给了 `<hero-detail>` 组件的 `hero` 属性。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="NgFor-1-2" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -2566,7 +2566,7 @@ The `index` property of the `NgForOf` directive context returns the zero-based i
|
|||
You can capture the `index` in a template input variable and use it in the template.
|
||||
|
||||
`NgFor` 指令上下文中的 `index` 属性返回一个从零开始的索引,表示当前条目在迭代中的顺序。
|
||||
我们可以通过模板输入变量捕获这个 `index` 值,并把它用在模板中。
|
||||
你可以通过模板输入变量捕获这个 `index` 值,并把它用在模板中。
|
||||
|
||||
The next example captures the `index` in a variable named `i` and displays it with the hero name like this.
|
||||
|
||||
|
@ -2599,15 +2599,14 @@ A small change to one item, an item removed, or an item added can trigger a casc
|
|||
|
||||
For example, re-querying the server could reset the list with all new hero objects.
|
||||
|
||||
例如,我们可以通过重新从服务器查询来刷新英雄列表。
|
||||
刷新后的列表可能包含很多(如果不是全部的话)以前显示过的英雄。
|
||||
例如,重新从服务器查询可以刷新包括所有新英雄在内的英雄列表。
|
||||
|
||||
Most, if not all, are previously displayed heroes.
|
||||
*You* know this because the `id` of each hero hasn't changed.
|
||||
But Angular sees only a fresh list of new object references.
|
||||
It has no choice but to tear down the old DOM elements and insert all new DOM elements.
|
||||
|
||||
他们中的绝大多数(如果不是所有的话)都是以前显示过的英雄。*我们*知道这一点,是因为每个英雄的 `id` 没有变化。
|
||||
他们中的绝大多数(如果不是所有的话)都是以前显示过的英雄。*你*知道这一点,是因为每个英雄的 `id` 没有变化。
|
||||
但在 Angular 看来,它只是一个由新的对象引用构成的新列表,
|
||||
它没有选择,只能清理旧列表、舍弃那些 DOM 元素,并且用新的 DOM 元素来重建一个新列表。
|
||||
|
||||
|
@ -2616,8 +2615,8 @@ Add a method to the component that returns the value `NgForOf` _should_ track.
|
|||
In this case, that value is the hero's `id`.
|
||||
|
||||
如果给它指定一个 `trackBy`,Angular 就可以避免这种折腾。
|
||||
我们往组件中添加一个方法,它会返回 `NgFor`*应该*追踪的值。
|
||||
在这里,这个值就是英雄的 `id`。
|
||||
往组件中添加一个方法,它会返回 `NgFor`*应该*追踪的值。
|
||||
在这里,这个值就是英雄的 `id`。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.ts" region="trackByHeroes" title="src/app/app.component.ts" linenums="false">
|
||||
|
||||
|
@ -2760,7 +2759,7 @@ You can refer to a template reference variable _anywhere_ in the template.
|
|||
The `phone` variable declared on this `<input>` is
|
||||
consumed in a `<button>` on the other side of the template
|
||||
|
||||
我们可以在模板中的任何地方引用模板引用变量。
|
||||
你可以在模板中的任何地方引用模板引用变量。
|
||||
比如声明在 `<input>` 上的 `phone` 变量就是在模板另一侧的 `<button>` 上使用的。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="ref-phone" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -2812,7 +2811,7 @@ But the `NgForm` directive does, which explains how you can disable the submit b
|
|||
if the `heroForm.form.valid` is invalid and pass the entire form control tree
|
||||
to the parent component's `onSubmit` method.
|
||||
|
||||
原生的 `<form>` 元素没有 `form` 属性,但 `NgForm` 指令有。这就解释了为何当 `heroForm.form.valid` 是无效时我们可以禁用提交按钮,
|
||||
原生的 `<form>` 元素没有 `form` 属性,但 `NgForm` 指令有。这就解释了为何当 `heroForm.form.valid` 是无效时你可以禁用提交按钮,
|
||||
并能把整个表单控件树传给父组件的 `onSubmit` 方法。
|
||||
|
||||
<h3 class="no-toc">Template reference variable warning notes</h3>
|
||||
|
@ -2836,7 +2835,7 @@ The runtime value will be unpredictable.
|
|||
You can use the `ref-` prefix alternative to `#`.
|
||||
This example declares the `fax` variable as `ref-fax` instead of `#fax`.
|
||||
|
||||
我们也可以用 `ref-` 前缀代替 `#`。
|
||||
你也可以用 `ref-` 前缀代替 `#`。
|
||||
下面的例子中就用把 `fax` 变量声明成了 `ref-fax` 而不是 `#fax`。
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="ref-fax" title="src/app/app.component.html" linenums="false">
|
||||
|
@ -2866,7 +2865,7 @@ Values flow _out_ of the component as events bound with an [event binding](#even
|
|||
|
||||
You can only bind to _another_ component or directive through its _Input_ and _Output_ properties.
|
||||
|
||||
我们只能通过它的**输入**和**输出**属性将其绑定到**其它**组件。
|
||||
你只能通过它的**输入**和**输出**属性将其绑定到**其它**组件。
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
|
@ -3266,7 +3265,7 @@ Throwing an exception is the right thing to do.
|
|||
On the other hand, null values in the property path may be OK from time to time,
|
||||
especially when the data are null now and will arrive eventually.
|
||||
|
||||
另一方面,属性路径中的空值可能会时常发生,特别是当我们知道数据最终会出现。
|
||||
另一方面,属性路径中的空值可能会时常发生,特别是数据目前为空但最终会出现。
|
||||
|
||||
While waiting for data, the view should render without complaint, and
|
||||
the null property path should display as blank just as the `title` property does.
|
||||
|
@ -3326,7 +3325,7 @@ It works perfectly with long property paths such as `a?.b?.c?.d`.
|
|||
|
||||
As of Typescript 2.0, you can enforce [strict null checking](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html "Strict null checking in TypeScript") with the `--strictNullChecks` flag. TypeScript then ensures that no variable is _unintentionally_ null or undefined.
|
||||
|
||||
在 TypeScript 2.0 中,我们可以使用 `--strictNullChecks` 标志强制开启[严格空值检查](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html "Strict null checking in TypeScript")。TypeScript 就会确保不存在意料之外的 null 或 undefined。
|
||||
在 TypeScript 2.0 中,你可以使用 `--strictNullChecks` 标志强制开启[严格空值检查](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html "Strict null checking in TypeScript")。TypeScript 就会确保不存在意料之外的 null 或 undefined。
|
||||
|
||||
In this mode, typed variables disallow null and undefined by default. The type checker throws an error if you leave a variable unassigned or try to assign null or undefined to a variable whose type disallows null and undefined.
|
||||
|
||||
|
@ -3338,8 +3337,8 @@ You tell the type checker that it can't happen by applying the post-fix
|
|||
[_non-null assertion operator (!)_](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator "Non-null assertion operator").
|
||||
|
||||
如果类型检查器在运行期间无法确定一个变量是 null 或 undefined,那么它也会抛出一个错误。
|
||||
我们自己可能知道它不会为空,但类型检查器不知道。
|
||||
所以我们要告诉类型检查器,它不会为空,这时就要用到[*非空断言操作符*](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator "Non-null assertion operator")。
|
||||
你自己可能知道它不会为空,但类型检查器不知道。
|
||||
所以你要告诉类型检查器,它不会为空,这时就要用到[*非空断言操作符*](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator "Non-null assertion operator")。
|
||||
|
||||
The _Angular_ **non-null assertion operator (`!`)** serves the same purpose in an Angular template.
|
||||
|
||||
|
@ -3368,7 +3367,7 @@ Rather it tells the TypeScript type checker to suspend strict null checks for a
|
|||
|
||||
You'll need this template operator when you turn on strict null checks. It's optional otherwise.
|
||||
|
||||
如果我们打开了严格控制检测,那就要用到这个模板操作符,而其它情况下则是可选的。
|
||||
如果你打开了严格控制检测,那就要用到这个模板操作符,而其它情况下则是可选的。
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
|
@ -3419,4 +3418,4 @@ You've completed this survey of template syntax.
|
|||
Now it's time to put that knowledge to work on your own components and directives.
|
||||
|
||||
|
||||
我们完成了模板语法的概述。现在,该把如何写组件和指令的知识投入到实际工作当中了。
|
||||
你完成了模板语法的概述。现在,该把如何写组件和指令的知识投入到实际工作当中了。
|
||||
|
|
|
@ -747,7 +747,7 @@ For now, you can radically reduce this test file to a more manageable size:
|
|||
In this example, the metadata object passed to `TestBed.configureTestingModule`
|
||||
simply declares `BannerComponent`, the component to test.
|
||||
|
||||
在这个例子中,传给 `TestBed.configureTestingModule` 的元数据对象中只声明了 `BannerComponent` —— 我们要测试的组件。
|
||||
在这个例子中,传给 `TestBed.configureTestingModule` 的元数据对象中只声明了 `BannerComponent` —— 待测试的组件。
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
|
@ -2206,7 +2206,7 @@ The immediate goal is to test the `DashboardHeroComponent`, not the `DashboardCo
|
|||
so, try the second and third options.
|
||||
|
||||
当前的任务是测试 `DashboardHeroComponent` 组件,而非 `DashbaordComponent`,所以无需做不必要的努力。
|
||||
让我们尝试第二和第三种方案。
|
||||
那就试试第二和第三种方案。
|
||||
|
||||
{@a dashboard-standalone}
|
||||
|
||||
|
@ -3676,7 +3676,7 @@ that intercepts server requests and fakes their responses.
|
|||
What if you aren't so lucky. What if faking the `HeroService` is hard?
|
||||
What if `HeroDetailService` makes its own server requests?
|
||||
|
||||
如果我们没有这么幸运怎么办?如果伪造 `HeroService` 很难怎么办?如果 `HeroDetailService` 自己发出服务器请求怎么办?
|
||||
如果你没有这么幸运怎么办?如果伪造 `HeroService` 很难怎么办?如果 `HeroDetailService` 自己发出服务器请求怎么办?
|
||||
|
||||
The `TestBed.overrideComponent` method can replace the component's `providers` with easy-to-manage _test doubles_
|
||||
as seen in the following setup variation:
|
||||
|
@ -3752,7 +3752,7 @@ It neither injects nor delegates to the lower level `HeroService`
|
|||
so there's no need to provide a test double for that.
|
||||
|
||||
`HeroDetailServiceSpy` 是实际 `HeroDetailService` 服务的桩版本,它伪造了该服务的所有必要特性。
|
||||
但它既不需要注入也不会委托给低层的 `HeroService` 服务,因此我们不用为 `HeroService` 提供测试替身。
|
||||
但它既不需要注入也不会委托给低层的 `HeroService` 服务,因此不用为 `HeroService` 提供测试替身。
|
||||
|
||||
The related `HeroDetailComponent` tests will assert that methods of the `HeroDetailService`
|
||||
were called by spying on the service methods.
|
||||
|
@ -3887,7 +3887,7 @@ in `By.css('h2:not([highlight])')` helps find `<h2>` elements that _do not_ have
|
|||
* `DebugElement.styles` affords access to element styles even in the absence of a real browser, thanks to the `DebugElement` abstraction.
|
||||
But feel free to exploit the `nativeElement` when that seems easier or more clear than the abstraction.
|
||||
|
||||
`DebugElement.styles` 让我们不借助真实的浏览器也可以访问元素的样式,感谢 `DebugElement` 提供的这层抽象!
|
||||
`DebugElement.styles` 甚至不用借助真实的浏览器也可以访问元素的样式,感谢 `DebugElement` 提供的这层抽象!
|
||||
但是如果直接使用 `nativeElement` 会比这层抽象更简单、更清晰,也可以放心大胆的使用它。
|
||||
|
||||
* Angular adds a directive to the injector of the element to which it is applied.
|
||||
|
@ -3898,7 +3898,7 @@ and its `defaultColor`.
|
|||
|
||||
* `DebugElement.properties` affords access to the artificial custom property that is set by the directive.
|
||||
|
||||
`DebugElement.properties` 让我们可以访问由指令设置的自定义属性。
|
||||
`DebugElement.properties` 让你可以访问由指令设置的自定义属性。
|
||||
|
||||
<hr>
|
||||
|
||||
|
@ -5285,7 +5285,7 @@ Angular 的 `By` 类为常用条件方法提供了三个静态方法:
|
|||
It's a good idea to put unit test spec files in the same folder
|
||||
as the application source code files that they test:
|
||||
|
||||
我们推荐将单元测试的 spec 配置文件放到与应用程序源代码文件所在的同一个文件夹中,因为:
|
||||
将单元测试的 spec 配置文件放到与应用程序源代码文件所在的同一个文件夹中是个好主意,因为:
|
||||
|
||||
* Such tests are easy to find.
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ that are important to Angular developers, including details about the following
|
|||
Typically, you add a TypeScript configuration file called `tsconfig.json` to your project to
|
||||
guide the compiler as it generates JavaScript files.
|
||||
|
||||
我们通常会往项目中加入一个 TypeScript 配置文件(`tsconfig.json`),来指导编译器如何生成 JavaScript 文件。
|
||||
你通常会往项目中加入一个 TypeScript 配置文件(`tsconfig.json`),来指导编译器如何生成 JavaScript 文件。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -49,7 +49,7 @@ For details about `tsconfig.json`, see the official
|
|||
|
||||
The [Setup](guide/setup) guide uses the following `tsconfig.json`:
|
||||
|
||||
我们在[搭建本地开发环境](guide/setup)中创建过如下的 `tsconfig.json`:
|
||||
在[搭建本地开发环境](guide/setup)中创建过如下的 `tsconfig.json`:
|
||||
|
||||
<code-example path="quickstart/src/tsconfig.1.json" title="tsconfig.json" linenums="false"></code-example>
|
||||
|
||||
|
@ -68,8 +68,8 @@ There is no correct answer and you can change the flag later.
|
|||
But your choice now can make a difference in larger projects, so it merits discussion.
|
||||
|
||||
TypeScript 开发者们在 `noImplicitAny` 标志应该是 `true` 还是 `false` 上存在分歧。
|
||||
这没有标准答案,我们以后还可以修改这个标志。
|
||||
但是我们的选择会在大项目中产生显著差异,所以它值得讨论一番。
|
||||
这没有标准答案,你以后还可以修改这个标志。
|
||||
但是你的选择会在大项目中产生显著差异,所以它值得讨论一番。
|
||||
|
||||
When the `noImplicitAny` flag is `false` (the default), and if
|
||||
the compiler cannot infer the variable type based on how it's used,
|
||||
|
@ -97,9 +97,9 @@ When the `noImplicitAny` flag is `true`, you may get *implicit index errors* as
|
|||
Most developers feel that *this particular error* is more annoying than helpful.
|
||||
You can suppress them with the following additional flag:
|
||||
|
||||
如果我们把 `noImplicitAny` 标志设置为了 `true`,我们可能会得到*隐式索引错*。
|
||||
如果把 `noImplicitAny` 标志设置为了 `true`,你可能会得到*隐式索引错*。
|
||||
大多数程序员可能觉得*这种错误*是个烦恼而不是助力。
|
||||
我们可以使用另一个标志来禁止它们。
|
||||
你可以使用另一个标志来禁止它们。
|
||||
|
||||
<code-example format=".">
|
||||
|
||||
|
@ -128,8 +128,8 @@ When the compiler doesn't recognize something, it throws an error.
|
|||
|
||||
Use [TypeScript type definition files](https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html)—`d.ts files`—to tell the compiler about the libraries you load.
|
||||
|
||||
我们可以使用[TypeScript 类型定义文件](https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html)
|
||||
—— `.d.ts` 文件 —— 来告诉编译器要加载的库的类型定义。
|
||||
可以使用[TypeScript 类型定义文件](https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html)
|
||||
—— `.d.ts` 文件 —— 来告诉编译器你要加载的库的类型定义。
|
||||
|
||||
TypeScript-aware editors leverage these same definition files to display type information about library features.
|
||||
|
||||
|
@ -145,7 +145,7 @@ The `node_modules/@angular/core/` folder of any Angular application contains sev
|
|||
**You need do nothing to get *typings* files for library packages that include `d.ts` files.
|
||||
Angular packages include them already.**
|
||||
|
||||
**我们不需要为那些包含了 `d.ts` 文件的库获取*类型定义*文件 —— Angular 的所有包都是如此。**
|
||||
**你不需要为那些包含了 `d.ts` 文件的库获取*类型定义*文件 —— Angular 的所有包都是如此。**
|
||||
|
||||
### lib.d.ts
|
||||
|
||||
|
@ -163,7 +163,7 @@ like `Promise` if the target is `es6`.
|
|||
Since the QuickStart is targeting `es5`, you can override the
|
||||
list of declaration files to be included:
|
||||
|
||||
因为《快速上手》的目标为 `es5`,所以我们可以重写声明文件列表来包含:
|
||||
因为《快速上手》的目标为 `es5`,所以你可以重写声明文件列表来包含:
|
||||
|
||||
<code-example format=".">
|
||||
|
||||
|
@ -173,7 +173,7 @@ list of declaration files to be included:
|
|||
|
||||
Thanks to that, you have all the `es6` typings even when targeting `es5`.
|
||||
|
||||
得益于这项设置,即使编译目标设置为 `es5`,我们也能获得所有的 `es6` 类型信息。
|
||||
得益于这项设置,即使编译目标设置为 `es5`,你也能获得所有的 `es6` 类型信息。
|
||||
|
||||
### Installable typings files
|
||||
|
||||
|
@ -190,16 +190,16 @@ You can install these typings via `npm` using the
|
|||
[`@types/*` scoped package](http://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)
|
||||
and Typescript, starting at 2.0, automatically recognizes them.
|
||||
|
||||
我们还可以通过 `npm` 来使用[`@types/*` 范围化包](http://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)来安装这些类型信息,
|
||||
你还可以通过 `npm` 来使用[`@types/*` 范围化包](http://www.typescriptlang.org/docs/handbook/declaration-files/consumption.html)来安装这些类型信息,
|
||||
而 TypeScript 自从 2.0 开始,可以自动识别它们。
|
||||
|
||||
For instance, to install typings for `jasmine` you could do `npm install @types/jasmine --save-dev`.
|
||||
|
||||
比如,要安装 `jasmine` 的类型信息,我们可以执行 `npm install @types/jasmine --save-dev`。
|
||||
比如,要安装 `jasmine` 的类型信息,你可以执行 `npm install @types/jasmine --save-dev`。
|
||||
|
||||
QuickStart identifies two *typings*, or `d.ts`, files:
|
||||
|
||||
我们在“快速上手”中指定过两个*类型定义*文件(`d.ts`):
|
||||
在“快速上手”中曾指定过两个*类型定义*文件(`d.ts`):
|
||||
|
||||
* [jasmine](http://jasmine.github.io/) typings for the Jasmine test framework.
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ tools. That means in addition to making the upgrade easier,
|
|||
you will also improve the existing AngularJS applications.
|
||||
|
||||
有些应用可能比其它的升级起来简单,还有一些方法能让把这项工作变得更简单。
|
||||
即使在正式开始升级过程之前,我们可以准备 AngularJS 的程序,让它向 Angular 看齐。
|
||||
即使在正式开始升级过程之前,可以提前准备 AngularJS 的程序,让它向 Angular 看齐。
|
||||
这些准备步骤几乎都是关于如何让代码更加松耦合、更有可维护性,以及用现代开发工具提高速度的。
|
||||
这意味着,这种准备工作不仅能让最终的升级变得更简单,而且还能提升 AngularJS 程序的质量。
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ You can find out more about TypeScript 2.2 support in Visual studio **[here](htt
|
|||
At this point, Visual Studio is ready. It’s a good idea to close Visual Studio and
|
||||
restart it to make sure everything is clean.
|
||||
|
||||
至此,Visual Studio 准备好了。重新启动 Visual Stuido,这样我们可以有一个崭新的开始。
|
||||
至此,Visual Studio 已经准备好了。重新启动 Visual Studio,这样就可以有一个崭新的开始了。
|
||||
|
||||
<h2 id='download'>Step 1: Download the QuickStart files</h2>
|
||||
|
||||
|
@ -275,7 +275,7 @@ Most Visual Studio developers like to press the F5 key and see the IIS server co
|
|||
To use the IIS server with the QuickStart app, you must make the following three changes.
|
||||
|
||||
大多数 Visual Studio 开发者喜欢按 F5 键来启动 IIS 服务器。
|
||||
要在这个《快速上手》应用中使用 IIS 服务器,我们要做下列修改:
|
||||
要在这个《快速上手》应用中使用 IIS 服务器,你要做下列修改:
|
||||
|
||||
1. In `index.html`, change base href from `<base href="/">` to `<base href="/src/">`.
|
||||
|
||||
|
@ -296,7 +296,7 @@ change the npm `path` to `/node_modules/` with a slash.
|
|||
After these changes, `npm start` no longer works.
|
||||
You must choose to configure _either_ for F5 with IIS _or_ for `npm start` with the lite-server.
|
||||
|
||||
做完这些修改之后,`npm start` 不再工作了。我们必须选择配置为 IIS + F5,还是 `npm start` + lite-server。
|
||||
做完这些修改之后,`npm start` 不再工作了。你必须选择配置为 IIS + F5,还是 `npm start` + lite-server。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -315,21 +315,21 @@ Everything seems fine while you move about _within_ the app.
|
|||
But you'll see the problem right away if you refresh the browser
|
||||
or paste a link to an app page (called a "deep link") into the browser address bar.
|
||||
|
||||
当我们在应用*内部*移动时,看起来一切正常。但是如果刷新浏览器,或者在地址栏中输入一个到具体页面的地址(也就是"深链接")时,问题就来了。
|
||||
当你在应用*内部*移动时,看起来一切正常。但是如果刷新浏览器,或者在地址栏中输入一个到具体页面的地址(也就是"深链接")时,问题就来了。
|
||||
|
||||
You'll most likely get a *404 - Page Not Found* response from the server
|
||||
for any address other than `/` or `/index.html`.
|
||||
|
||||
我们很可能从服务器得到得到*404 - 页面不存在* —— 只有 `/` 或 `/index.html` 例外。
|
||||
你很可能从服务器得到得到*404 - 页面不存在* —— 只有 `/` 或 `/index.html` 例外。
|
||||
|
||||
You have to configure the server to return `index.html` for requests to these "unknown" pages.
|
||||
The `lite-server` development server does out-of-the-box.
|
||||
If you've switched over to F5 and IIS, you have to configure IIS to do it.
|
||||
This section walks through the steps to adapt the QuickStart application.
|
||||
|
||||
我们就要配置服务器,为那些"未知"的页面返回 `index.html`。
|
||||
`lite-server` 开发服务器内置了这项功能。如果要切换到 F5 + IIS,我们就要自己来配置 IIS 实现它了。
|
||||
接下来我们就看看对快速起步应用做配置的步骤。
|
||||
你就要配置服务器,为那些"未知"的页面返回 `index.html`。
|
||||
`lite-server` 开发服务器内置了这项功能。如果要切换到 F5 + IIS,你就要自己来配置 IIS 实现它了。
|
||||
接下来看看对快速起步应用做配置的步骤。
|
||||
|
||||
#### Configure IIS rewrite rules
|
||||
|
||||
|
@ -371,7 +371,7 @@ rewrite rules near the bottom of the `web.config`:
|
|||
The match url, `<match url=".*" />`, will rewrite every request. You'll have to adjust this if
|
||||
you want some requests to get through, such as web API requests.
|
||||
|
||||
匹配 url `<match url=".*" />` 语句将会重写每一个请求。如果需要直接放行某些请求,比如一些 Web API 请求,我们就必须调整它才行。
|
||||
匹配 url `<match url=".*" />` 语句将会重写每一个请求。如果需要直接放行某些请求,比如一些 Web API 请求,你就必须调整它才行。
|
||||
|
||||
The URL in `<action type="Rewrite" url="/src/"/>` should
|
||||
match the base href in `index.html`.
|
||||
|
|
|
@ -18,7 +18,7 @@ and for loading that code from a server into a browser.
|
|||
It's an excellent alternative to the *SystemJS* approach used elsewhere in the documentation.
|
||||
This guide offers a taste of Webpack and explains how to use it with Angular applications.
|
||||
|
||||
它是我们在文档中到处使用的 *SystemJS* 的一个优秀替代品。这篇指南会带我们尝尝 Webpack 的滋味,并解释如何在 Angular 程序中使用它。
|
||||
它是这个文档中到处使用的 *SystemJS* 的一个优秀替代品。这篇指南会浅尝 Webpack,并解释如何在 Angular 程序中使用它。
|
||||
|
||||
{@a top}
|
||||
|
||||
|
@ -138,7 +138,7 @@ Webpack 会遍历你应用中的所有源码,查找 `import` 语句,构建
|
|||
|
||||
You determine what Webpack does and how it does it with a JavaScript configuration file, `webpack.config.js`.
|
||||
|
||||
我们通过一个 JavaScript 配置文件 `webpack.config.js` 来决定 Webpack 做什么以及如何做。
|
||||
你通过一个 JavaScript 配置文件 `webpack.config.js` 来决定 Webpack 做什么以及如何做。
|
||||
|
||||
{@a entries-outputs}
|
||||
|
||||
|
@ -149,8 +149,8 @@ You determine what Webpack does and how it does it with a JavaScript configurati
|
|||
You supply Webpack with one or more *entry* files and let it find and incorporate the dependencies that radiate from those entries.
|
||||
The one entry point file in this example is the application's root file, `src/main.ts`:
|
||||
|
||||
我们给 Webpack 提供一个或多个*入口*文件,来让它查找与合并那些从这些入口点发散出去的依赖。
|
||||
在下面这个例子中,我们的入口点是该应用的根文件 `src/app.ts`:
|
||||
你给 Webpack 提供一个或多个*入口*文件,来让它查找与合并那些从这些入口点发散出去的依赖。
|
||||
在下面这个例子中,唯一的入口点文件是该应用的根文件 `src/app.ts`:
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" region="one-entry" title="webpack.config.js (single entry)" linenums="false">
|
||||
|
||||
|
@ -167,7 +167,7 @@ Webpack 探查那个文件,并且递归遍历它的 `import` 依赖。
|
|||
It sees that you're importing `@angular/core` so it adds that to its dependency list for potential inclusion in the bundle.
|
||||
It opens the `@angular/core` file and follows _its_ network of `import` statements until it has built the complete dependency graph from `main.ts` down.
|
||||
|
||||
这里,Webpack 看到我们正在导入 `@angular/core`,于是就这个文件加入到它的依赖列表里,为(有可能)把该文件打进包中做准备。
|
||||
这里,Webpack 看到你正在导入 `@angular/core`,于是就这个文件加入到它的依赖列表里,为(有可能)把该文件打进包中做准备。
|
||||
它打开 `@angular/core` 并追踪由_该文件的_`import` 语句构成的网络,直到构建出从 `main.ts` 往下的整个依赖图谱。
|
||||
|
||||
Then it **outputs** these files to the `app.js` _bundle file_ designated in configuration:
|
||||
|
@ -186,7 +186,7 @@ This `app.js` output bundle is a single JavaScript file that contains the applic
|
|||
You'll load it later with a `<script>` tag in the `index.html`.
|
||||
|
||||
这个 `app.js` 输出包是个单一的 JavaScript 文件,它包含程序的源码及其所有依赖。
|
||||
后面我们将在 `index.html` 中用 `<script>` 标签来加载它。
|
||||
后面你将在 `index.html` 中用 `<script>` 标签来加载它。
|
||||
|
||||
{@a multiple-bundles}
|
||||
|
||||
|
@ -197,7 +197,7 @@ You'll load it later with a `<script>` tag in the `index.html`.
|
|||
You probably don't want one giant bundle of everything.
|
||||
It's preferable to separate the volatile application app code from comparatively stable vendor code modules.
|
||||
|
||||
我们可能不会希望把所有东西打进一个巨型包,而更喜欢把多变的应用代码从相对稳定的第三方提供商模块中分离出来。
|
||||
你可能不会希望把所有东西打进一个巨型包,而更喜欢把多变的应用代码从相对稳定的第三方提供商模块中分离出来。
|
||||
|
||||
Change the configuration so that it has two entry points, `main.ts` and `vendor.ts`:
|
||||
|
||||
|
@ -220,7 +220,7 @@ Webpack constructs two separate dependency graphs
|
|||
and emits *two* bundle files, one called `app.js` containing only the application code and
|
||||
another called `vendor.js` with all the vendor dependencies.
|
||||
|
||||
Webpack 会构造出两个独立的依赖图谱,并产出*两个*包文件:一个叫做 `app.js`,它只包含我们的应用代码;另一个叫做 `vendor.js`,它包含所有的提供商依赖。
|
||||
Webpack 会构造出两个独立的依赖图谱,并产出*两个*包文件:一个叫做 `app.js`,它只包含应用的代码;另一个叫做 `vendor.js`,它包含所有的提供商依赖。
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
@ -253,8 +253,8 @@ Configure loaders for TypeScript and CSS as follows.
|
|||
|
||||
Webpack 可以打包任何类型的文件:JavaScript、TypeScript、CSS、SASS、LESS、图片、HTML 以及字体文件等等。
|
||||
但 Webpack*本身*只认识 JavaScript 文件。
|
||||
我们要通过*加载器*来告诉它如何把这些文件处理成 JavaScript 文件。
|
||||
在这里,我们为 TypeScript 和 CSS 文件配置了加载器。
|
||||
要通过*加载器*来告诉它如何把这些文件处理成 JavaScript 文件。
|
||||
再为 TypeScript 和 CSS 文件配置如下加载器。
|
||||
|
||||
<code-example language="javascript">
|
||||
|
||||
|
@ -313,7 +313,7 @@ Webpack has a build pipeline with well-defined phases.
|
|||
Tap into that pipeline with plugins such as the `uglify` minification plugin:
|
||||
|
||||
Webpack 有一条构建流水线,它被划分成多个经过精心定义的阶段(phase)。
|
||||
我们可以把插件(比如 `uglify` 代码最小化插件)挂到流水线上:
|
||||
可以把插件(比如 `uglify` 代码最小化插件)挂到流水线上:
|
||||
|
||||
<code-example language="javascript">
|
||||
|
||||
|
@ -331,7 +331,7 @@ Webpack 有一条构建流水线,它被划分成多个经过精心定义的阶
|
|||
|
||||
After that brief orientation, you are ready to build your own Webpack configuration for Angular apps.
|
||||
|
||||
经过简短的培训之后,我们准备为 Angular 应用构建一份自己的 Webpack 配置了。
|
||||
经过简短的培训之后,你已经准备好为 Angular 应用构建一份自己的 Webpack 配置了。
|
||||
|
||||
Begin by setting up the development environment.
|
||||
|
||||
|
@ -410,12 +410,12 @@ Open a terminal window and install the npm packages.
|
|||
You'll need polyfills to run an Angular application in most browsers as explained
|
||||
in the [Browser Support](guide/browser-support) guide.
|
||||
|
||||
我们在[_浏览器支持_](guide/browser-support)章节里解释过,Angular 应用要能在大多数的浏览器里运行,它还需要一些 polyfills。
|
||||
要让 Angular 应用能在大多数的浏览器里运行,它还需要一些腻子脚本,参见[浏览器支持](guide/browser-support)。
|
||||
|
||||
Polyfills should be bundled separately from the application and vendor bundles.
|
||||
Add a `polyfills.ts` like this one to the `src/` folder.
|
||||
|
||||
Polyfills 最好跟应用代码和 vendor 代码区分开来单独打包,所以我们需要在 `src/` 文件夹里添加一个 `polyfills.ts` 文件,代码如下:
|
||||
Polyfills 最好跟应用代码和 vendor 代码区分开来单独打包。在 `src/` 文件夹里添加一个 `polyfills.ts` 文件,代码如下:
|
||||
|
||||
<code-example path="webpack/src/polyfills.ts" title="src/polyfills.ts" linenums="false">
|
||||
|
||||
|
@ -453,7 +453,7 @@ All three have a lot of configuration in common.
|
|||
|
||||
Gather the common configuration in a file called `webpack.common.js`.
|
||||
|
||||
我们可以把这些通用的配置收归到一个文件,命名为 `webpack.common.js`。
|
||||
把这些通用的配置收归到一个文件,命名为 `webpack.common.js`。
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" title="config/webpack.common.js" linenums="false">
|
||||
|
||||
|
@ -541,7 +541,7 @@ But most `import` statements don't mention the extension at all.
|
|||
Tell Webpack to resolve extension-less file requests by looking for matching files with
|
||||
`.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files).
|
||||
|
||||
但实际上大部分 `import` 语句都不带扩展名,我们可以告诉 Webpack,在查找这些没有扩展名的文件时,自动加上 `.ts` 或者 `.js` 扩展名来匹配。
|
||||
但实际上大部分 `import` 语句都不带扩展名,可以告诉 Webpack,在查找这些没有扩展名的文件时,自动加上 `.ts` 或者 `.js` 扩展名来匹配。
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" region="resolve" title="config/webpack.common.js" linenums="false">
|
||||
|
||||
|
@ -552,7 +552,7 @@ Tell Webpack to resolve extension-less file requests by looking for matching fil
|
|||
If Webpack should resolve extension-less files for styles and HTML,
|
||||
add `.css` and `.html` to the list.
|
||||
|
||||
如果我们希望 Webapck 也能解析不带扩展名的样式和 HTML 文件,在列表里追加 `.css` 和 `.html` 即可。
|
||||
如果要让 Webapck 也能解析那些不带扩展名的样式和 HTML 文件,在列表里追加 `.css` 和 `.html` 即可。
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -597,7 +597,7 @@ The first pattern is for the application-wide styles. It excludes `.css` files w
|
|||
where the component-scoped styles sit. The `ExtractTextPlugin` (described below) applies the `style` and `css`
|
||||
loaders to these files.
|
||||
|
||||
第一个模式是给全局样式使用的,它排除了 `/src/app` 目录下的 `.css` 文件,因为那里放着我们的组件局部样式。
|
||||
第一个模式是给全局样式使用的,它排除了 `/src/app` 目录下的 `.css` 文件,因为那里是组件的局部样式。
|
||||
它只包含了那些位于 `/src/app` 及其上级目录的 `.css` 文件,那里是应用级样式。
|
||||
`ExtractTextPlugin`(后面会讲到)使用 `style` 和 `css` 加载器来处理这些文件。
|
||||
|
||||
|
@ -671,8 +671,8 @@ You _could_ insert them into the `index.html` _manually_. That would be tedious
|
|||
Webpack can inject those scripts and links for you with the `HtmlWebpackPlugin`.
|
||||
|
||||
Webpack 生成了一些 js 和 css 文件。
|
||||
虽然我们_可以手动_把它们插入到 `index.html` 中,但那样既枯燥又容易出错。
|
||||
Webpack 可以通过 `HtmlWebpackPlugin` 自动为我们注入那些 `script` 和 `link` 标签。
|
||||
虽然你_可以手动_把它们插入到 `index.html` 中,但那样既枯燥又容易出错。
|
||||
Webpack 可以通过 `HtmlWebpackPlugin` 自动为你注入那些 `script` 和 `link` 标签。
|
||||
|
||||
{@a environment-configuration}
|
||||
|
||||
|
@ -685,7 +685,7 @@ Create separate, environment-specific configuration files that build on `webpack
|
|||
by merging into it the peculiarities particular to the target environments.
|
||||
|
||||
`webpack.common.js` 配置做了大部分繁重的工作。
|
||||
通过合并它们特有的配置,我们可以基于 `webpack.common` 为目标环境创建独立的、环境相关的配置文件。
|
||||
通过合并它们特有的配置,就可以基于 `webpack.common` 为目标环境创建独立的、环境相关的配置文件。
|
||||
|
||||
These files tend to be short and simple.
|
||||
|
||||
|
@ -707,13 +707,13 @@ Here is the `webpack.dev.js` development configuration file.
|
|||
|
||||
The development build relies on the Webpack development server, configured near the bottom of the file.
|
||||
|
||||
开发环境下的构建依赖于 Webpack 的开发服务器,我们在靠近文件底部的地方配置了它。
|
||||
开发环境下的构建依赖于 Webpack 的开发服务器,它是在靠近文件底部的地方配置的。
|
||||
|
||||
Although you tell Webpack to put output bundles in the `dist` folder,
|
||||
the dev server keeps all bundles in memory; it doesn't write them to disk.
|
||||
You won't find any files in the `dist` folder, at least not any generated from *this development build*.
|
||||
|
||||
虽然我们告诉 Webpack 把输出包放到 `dist` 目录,但实际上开发服务器把这些包都放在了内存里,而不会把它们写到硬盘中。
|
||||
虽然你告诉 Webpack 把输出包放到 `dist` 目录,但实际上开发服务器把这些包都放在了内存里,而不会把它们写到硬盘中。
|
||||
所以在 `dist` 目录下是找不到任何文件的(至少现在这个开发环境下构建时没有)。
|
||||
|
||||
The `HtmlWebpackPlugin`, added in `webpack.common.js`, uses the `publicPath` and the `filename` settings to generate
|
||||
|
@ -725,7 +725,7 @@ appropriate `<script>` and `<link>` tags into the `index.html`.
|
|||
The CSS styles are buried inside the Javascript bundles by default. The `ExtractTextPlugin` extracts them into
|
||||
external `.css` files that the `HtmlWebpackPlugin` inscribes as `<link>` tags into the `index.html`.
|
||||
|
||||
默认情况下,我们这些 CSS 样式会被埋没在 JavaScript 包中。`ExtractTextPlugin` 会把它们提取成外部 `.css` 文件,
|
||||
默认情况下,这些 CSS 样式会被埋没在 JavaScript 包中。`ExtractTextPlugin` 会把它们提取成外部 `.css` 文件,
|
||||
这样 `HtmlWebpackPlugin` 插件就会转而把一个<link>标签写进 `index.html` 了。Refer to the [Webpack documentation](https://webpack.github.io/docs/) for details on these and
|
||||
other configuration options in this file.要了解本文件中这些以及其它配置项的详情,请参阅[Webpack 文档](https://webpack.github.io/docs/)。
|
||||
|
||||
|
@ -761,7 +761,7 @@ Configuration of a *production* build resembles *development* configuration with
|
|||
You'll deploy the application and its dependencies to a real production server.
|
||||
You won't deploy the artifacts needed only in development.
|
||||
|
||||
我们希望把应用程序及其依赖都部署到一个真实的产品服务器中。
|
||||
你希望把应用程序及其依赖都部署到一个真实的产品服务器中。
|
||||
而不希望部署那些只在开发环境下才用得到的依赖。
|
||||
|
||||
Put the production output bundle files in the `dist` folder.
|
||||
|
@ -772,7 +772,7 @@ Webpack generates file names with cache-busting hash.
|
|||
Thanks to the `HtmlWebpackPlugin`, you don't have to update the `index.html` file when the hash changes.
|
||||
|
||||
Webpack 生成的文件名中带有“缓存无效哈希(cache-busting hash)”。
|
||||
感谢 `HtmlWebpackPlugin` 插件,当这些哈希值变化时,我们不用去更新 `index.html` 了。
|
||||
感谢 `HtmlWebpackPlugin` 插件,当这些哈希值变化时,你就不用去更新 `index.html` 了。
|
||||
|
||||
There are additional plugins:
|
||||
|
||||
|
@ -792,7 +792,7 @@ There are additional plugins:
|
|||
|
||||
* *`DefinePlugin`—use to define environment variables that you can reference within the application.
|
||||
|
||||
*`DefinePlugin`* - 用来定义环境变量,以便我们在自己的程序中引用它。
|
||||
*`DefinePlugin`* - 用来定义环境变量,以便你在自己的程序中引用它。
|
||||
|
||||
* *`LoaderOptionsPlugins`—to override options of certain loaders.
|
||||
|
||||
|
@ -800,7 +800,7 @@ There are additional plugins:
|
|||
|
||||
Thanks to the `DefinePlugin` and the `ENV` variable defined at top, you can enable Angular production mode like this:
|
||||
|
||||
感谢 *DefinePlugin* 和顶部定义的 `ENV` 变量,我们就可以像这样启用 Angular 的产品模式了:
|
||||
感谢 *DefinePlugin* 和顶部定义的 `ENV` 变量,你就可以像这样启用 Angular 的产品模式了:
|
||||
|
||||
<code-example path="webpack/src/main.ts" region="enable-prod" title="src/main.ts" linenums="false">
|
||||
|
||||
|
@ -827,14 +827,14 @@ You don't need the loaders and plugins that you declared for your development an
|
|||
You probably don't need to load and process the application-wide styles files for unit tests and doing so would slow you down;
|
||||
you'll use the `null` loader for those CSS files.
|
||||
|
||||
我们并不需要使用很多配置项来运行单元测试。
|
||||
你并不需要使用很多配置项来运行单元测试。
|
||||
也不需要在开发环境和产品环境下引入的那些加载器和插件。
|
||||
如果有可能拖慢执行速度,甚至都不需要在单元测试中加载和处理应用全局样式文件,所以我们用一个 `null` 加载器来处理所有 CSS。
|
||||
如果有可能拖慢执行速度,甚至都不需要在单元测试中加载和处理应用全局样式文件,所以你用一个 `null` 加载器来处理所有 CSS。
|
||||
|
||||
You could merge the test configuration into the `webpack.common` configuration and override the parts you don't want or need.
|
||||
But it might be simpler to start over with a completely fresh configuration.
|
||||
|
||||
我们可以把测试环境的配置合并到 `webpack.common` 配置中,并且改写不想要或不需要的部分。
|
||||
你可以把测试环境的配置合并到 `webpack.common` 配置中,并且改写不想要或不需要的部分。
|
||||
但是从一个全新的配置开始可能更简单。
|
||||
|
||||
<code-example path="webpack/config/webpack.test.js" title="config/webpack.test.js" linenums="false">
|
||||
|
@ -852,7 +852,7 @@ Reconfigure [Karma](https://karma-runner.github.io/1.0/index.html) to use Webpac
|
|||
You don't precompile the TypeScript; Webpack transpiles the Typescript files on the fly, in memory, and feeds the emitted JS directly to Karma.
|
||||
There are no temporary files on disk.
|
||||
|
||||
我们不用预编译 TypeScript,Webpack 随时在内存中转译我们的 TypeScript 文件,并且把产出的 JS 直接反馈给 Karma。
|
||||
你不用预编译 TypeScript,Webpack 随时在内存中转译我们的 TypeScript 文件,并且把产出的 JS 直接反馈给 Karma。
|
||||
硬盘上没有任何临时文件。
|
||||
|
||||
The `karma-test-shim` tells Karma what files to pre-load and
|
||||
|
@ -869,10 +869,10 @@ You tell Webpack to find and load the test files (the files ending in `.spec.ts`
|
|||
Each spec file imports all—and only—the application source code that it tests.
|
||||
Webpack loads just _those_ specific application files and ignores the other files that you aren't testing.
|
||||
|
||||
注意,我们_并没有_明确加载这些应用代码。
|
||||
只是告诉 Webpack 查找并加载我们的测试文件(文件名以 `.spec.ts` 结尾)。
|
||||
注意,你_并没有_明确加载这些应用代码。
|
||||
只是告诉 Webpack 查找并加载这些测试文件(文件名以 `.spec.ts` 结尾)。
|
||||
每个规约(spec)文件都导入了所有(也只有)它测试所需的应用源码。
|
||||
Webpack 只加载_那些_特定的应用文件,而忽略所有其它我们不会测试到的。
|
||||
Webpack 只加载_那些_特定的应用文件,而忽略所有其它你不会测试到的。
|
||||
|
||||
Grab the app code at the end of this guide and try:
|
||||
|
||||
|
@ -893,7 +893,7 @@ Grab the app code at the end of this guide and try:
|
|||
Here is the source code for a small application that bundles with the
|
||||
Webpack techniques covered in this guide.
|
||||
|
||||
这里是一个小型应用的全部源码,我们可以用本章中学到的 Webpack 技术打包它们。
|
||||
这里是一个小型应用的全部源码,可以用本章中学到的 Webpack 技术打包它们。
|
||||
|
||||
<code-tabs>
|
||||
|
||||
|
@ -986,7 +986,7 @@ You don't see those calls in the source code;
|
|||
they're added behind the scenes by the `angular2-template-loader` plug-in.
|
||||
|
||||
`AppComponent` 组件本身有它自己的 HTML 模板和 CSS 文件。Webpack 通过调用 `require()` 方法加载它们。Webpack 还把那些组件内部的文件打包进了 `app.js` 中。
|
||||
我们在自己的源码中看不到这些调用,这些工作是由幕后的 `angular2-template-loader` 插件完成的。
|
||||
你在自己的源码中看不到这些调用,这些工作是由幕后的 `angular2-template-loader` 插件完成的。
|
||||
|
||||
* The `vendor.ts` consists of vendor dependency `import` statements that drive the `vendor.js` bundle.
|
||||
The application imports these modules too; they'd be duplicated in the `app.js` bundle
|
||||
|
@ -1004,11 +1004,11 @@ if the `CommonsChunkPlugin` hadn't detected the overlap and removed them from `a
|
|||
You've learned just enough Webpack to configurate development, test and production builds
|
||||
for a small Angular application.
|
||||
|
||||
我们学到了刚好够用来在开发、测试、产品环境下构建一个小型 Angular 应用的 Webpack 配置知识。
|
||||
你学到了刚好够用来在开发、测试、产品环境下构建一个小型 Angular 应用的 Webpack 配置知识。
|
||||
|
||||
_You could always do more_. Search the web for expert advice and expand your Webpack knowledge.
|
||||
|
||||
_但我们还能做得更多_。搜索互联网来获得专家的建议,并扩展你对 Webpack 的认识。
|
||||
_但你还能做得更多_。搜索互联网来获得专家的建议,并扩展你对 Webpack 的认识。
|
||||
|
||||
[Back to top](guide/webpack#top)
|
||||
|
||||
|
|
|
@ -390,7 +390,7 @@
|
|||
},
|
||||
{
|
||||
"url": "guide/aot-compiler",
|
||||
"title": "预(AOT)编译",
|
||||
"title": "预先(AOT)编译",
|
||||
"tooltip": "学习如何使用 AOT 预编译器。"
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue