修订完component-communication

This commit is contained in:
Zhicheng Wang 2017-04-15 17:09:42 +08:00
parent f656c40847
commit 896e5b6ce1
3 changed files with 105 additions and 11 deletions

View File

@ -102,6 +102,7 @@ table(width="100%")
section of the [Template Syntax](../guide/template-syntax.html) page. section of the [Template Syntax](../guide/template-syntax.html) page.
要了解更多,请参见[模板语法](../guide/template-syntax.html)中的[插值表达式](../guide/template-syntax.html#interpolation)部分。 要了解更多,请参见[模板语法](../guide/template-syntax.html)中的[插值表达式](../guide/template-syntax.html#interpolation)部分。
tr(style=top) tr(style=top)
td td
:marked :marked
@ -317,6 +318,7 @@ table(width="100%")
### 绑定到`click`事件 ### 绑定到`click`事件
+makeExample('cb-ajs-quick-reference/ts/src/app/app.component.html', 'event-binding')(format="." ) +makeExample('cb-ajs-quick-reference/ts/src/app/app.component.html', 'event-binding')(format="." )
:marked :marked
AngularJS event-based directives do not exist in Angular. AngularJS event-based directives do not exist in Angular.
Rather, define one-way binding from the template view to the component using **event binding**. Rather, define one-way binding from the template view to the component using **event binding**.
@ -588,7 +590,9 @@ table(width="100%")
### Bind to the `hidden` property ### Bind to the `hidden` property
### 绑定到`hidden`属性 ### 绑定到`hidden`属性
+makeExample('cb-ajs-quick-reference/ts/src/app/movie-list.component.html', 'hidden')(format="." ) +makeExample('cb-ajs-quick-reference/ts/src/app/movie-list.component.html', 'hidden')(format="." )
:marked :marked
Angular uses property binding; there is no built-in *show* directive. Angular uses property binding; there is no built-in *show* directive.
For hiding and showing elements, bind to the HTML `hidden` property. For hiding and showing elements, bind to the HTML `hidden` property.
@ -796,6 +800,7 @@ table(width="100%")
Formats a number as currency. Formats a number as currency.
把一个数字格式化成货币。 把一个数字格式化成货币。
td td
:marked :marked
### currency ### currency

View File

@ -229,7 +229,10 @@ a#compile
)(format='.') )(format='.')
:marked :marked
Install a few new npm dependencies with the following command: 用下列命令安装少量新的npm依赖 Install a few new npm dependencies with the following command:
用下列命令安装少量新的npm依赖
code-example(language="none" class="code-shell"). code-example(language="none" class="code-shell").
npm install @angular/compiler-cli @angular/platform-server --save npm install @angular/compiler-cli @angular/platform-server --save
:marked :marked
@ -342,6 +345,7 @@ a#bootstrap
.l-main-section .l-main-section
:marked :marked
## Bootstrap ## Bootstrap
## 引导 ## 引导
The AOT approach changes application bootstrapping. The AOT approach changes application bootstrapping.
@ -427,6 +431,7 @@ a#rollup
Rollup can only tree shake `ES2015` modules which have `import` and `export` statements. Rollup can only tree shake `ES2015` modules which have `import` and `export` statements.
Rollup只能对`ES2015`模块摇树,因为那里有`import`和`export`语句。 Rollup只能对`ES2015`模块摇树,因为那里有`import`和`export`语句。
.l-sub-section .l-sub-section
:marked :marked
Recall that `tsconfig-aot.json` is configured to produce `ES2015` modules. Recall that `tsconfig-aot.json` is configured to produce `ES2015` modules.
@ -514,6 +519,7 @@ a#rollup-plugins
Rollup做摇树优化时会大幅减小代码体积。最小化过程则会让它更小。 Rollup做摇树优化时会大幅减小代码体积。最小化过程则会让它更小。
本烹饪宝典依赖于Rollup插件*uglify*来最小化并混淆代码。 本烹饪宝典依赖于Rollup插件*uglify*来最小化并混淆代码。
把下列代码添加到`plugins`数组中:
+makeExample('cb-aot-compiler/ts/rollup-config.js','uglify','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.') +makeExample('cb-aot-compiler/ts/rollup-config.js','uglify','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.')
@ -855,6 +861,7 @@ code-example(language="none" class="code-shell").
Now AOT-compile the app and launch it with the `lite-server`: Now AOT-compile the app and launch it with the `lite-server`:
现在AOT编译应用并使用`lite`服务器启动它: 现在AOT编译应用并使用`lite`服务器启动它:
code-example(language="none" class="code-shell"). code-example(language="none" class="code-shell").
npm run build:aot && npm run serve:aot npm run build:aot && npm run serve:aot

View File

@ -50,30 +50,45 @@ include ../_util-fns
<a id="parent-to-child"></a> <a id="parent-to-child"></a>
:marked :marked
## Pass data from parent to child with input binding ## Pass data from parent to child with input binding
## 通过输入型绑定把数据从父组件传到子组件。 ## 通过输入型绑定把数据从父组件传到子组件。
`HeroChildComponent` has two ***input properties***, `HeroChildComponent` has two ***input properties***,
typically adorned with [@Input decorations](../guide/template-syntax.html#inputs-outputs). typically adorned with [@Input decorations](../guide/template-syntax.html#inputs-outputs).
`HeroChildComponent` 有两个***输入型属性***,它们通常带[@Input装饰器](../guide/template-syntax.html#inputs-outputs)。 `HeroChildComponent` 有两个***输入型属性***,它们通常带[@Input装饰器](../guide/template-syntax.html#inputs-outputs)。
+makeExample('cb-component-communication/ts/src/app/hero-child.component.ts') +makeExample('cb-component-communication/ts/src/app/hero-child.component.ts')
:marked :marked
The second `@Input` aliases the child component property name `masterName` as `'master'`. The second `@Input` aliases the child component property name `masterName` as `'master'`.
第二个`@Input`为子组件的属性名`masterName`指定一个别名`master`(译者注:不推荐为起别名,请参见风格指南). 第二个`@Input`为子组件的属性名`masterName`指定一个别名`master`(译者注:不推荐为起别名,请参见风格指南).
The `HeroParentComponent` nests the child `HeroChildComponent` inside an `*ngFor` repeater, The `HeroParentComponent` nests the child `HeroChildComponent` inside an `*ngFor` repeater,
binding its `master` string property to the child's `master` alias binding its `master` string property to the child's `master` alias, and each iteration's `hero` instance to the child's `hero` property.
, and each iteration's `hero` instance to the child's `hero` property.父组件`HeroParentComponent`把子组件的`HeroChildComponent`放到`*ngFor`循环器中,把自己的`master`字符串属性绑定到子组件的`master`别名上,并把每个循环的`hero`实例绑定到子组件的`hero`属性。
父组件`HeroParentComponent`把子组件的`HeroChildComponent`放到`*ngFor`循环器中,把自己的`master`字符串属性绑定到子组件的`master`别名上,并把每个循环的`hero`实例绑定到子组件的`hero`属性。
+makeExample('cb-component-communication/ts/src/app/hero-parent.component.ts') +makeExample('cb-component-communication/ts/src/app/hero-parent.component.ts')
:marked :marked
The running application displays three heroes: The running application displays three heroes:
运行应用程序会显示三个英雄: 运行应用程序会显示三个英雄:
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child") img(src="/resources/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child")
:marked :marked
### Test it ### Test it
### 测试 ### 测试
E2E test that all children were instantiated and displayed as expected: E2E test that all children were instantiated and displayed as expected:
端到端测试,用于确保所有的子组件都像所期待的那样被初始化并显示出来。 端到端测试,用于确保所有的子组件都像所期待的那样被初始化并显示出来。
+makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child') +makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child')
:marked :marked
@ -89,10 +104,14 @@ figure.image-display
## 通过setter截听输入属性值的变化 ## 通过setter截听输入属性值的变化
Use an input property setter to intercept and act upon a value from the parent. Use an input property setter to intercept and act upon a value from the parent.
使用一个输入属性的setter以拦截父组件中值的变化并采取行动。 使用一个输入属性的setter以拦截父组件中值的变化并采取行动。
The setter of the `name` input property in the child `NameChildComponent` The setter of the `name` input property in the child `NameChildComponent`
trims the whitespace from a name and replaces an empty value with default text. trims the whitespace from a name and replaces an empty value with default text.
子组件`NameChildComponent`的输入属性`name`上的这个setter会trim掉名字里的空格并把空值替换成默认字符串。 子组件`NameChildComponent`的输入属性`name`上的这个setter会trim掉名字里的空格并把空值替换成默认字符串。
+makeExample('cb-component-communication/ts/src/app/name-child.component.ts') +makeExample('cb-component-communication/ts/src/app/name-child.component.ts')
:marked :marked
@ -107,10 +126,13 @@ figure.image-display
:marked :marked
### Test it ### Test it
### 测试 ### 测试
E2E tests of input property setter with empty and non-empty names: E2E tests of input property setter with empty and non-empty names:
端到端测试输入属性的setter分别使用空名字和非空名字。 端到端测试输入属性的setter分别使用空名字和非空名字。
+makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-setter') +makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-setter')
:marked :marked
@ -122,7 +144,8 @@ figure.image-display
<a id="parent-to-child-on-changes"></a> <a id="parent-to-child-on-changes"></a>
:marked :marked
## Intercept input property changes with *ngOnChanges()* ## Intercept input property changes with *ngOnChanges()*
## 通过*ngOnChanges*来截听输入属性值的变化
## 通过*ngOnChanges()*来截听输入属性值的变化
Detect and act upon changes to input property values with the `ngOnChanges()` method of the `OnChanges` lifecycle hook interface. Detect and act upon changes to input property values with the `ngOnChanges()` method of the `OnChanges` lifecycle hook interface.
@ -136,30 +159,40 @@ figure.image-display
Learn about `ngOnChanges()` in the [LifeCycle Hooks](../guide/lifecycle-hooks.html) chapter. Learn about `ngOnChanges()` in the [LifeCycle Hooks](../guide/lifecycle-hooks.html) chapter.
学习关于`ngOnChanges`的更多知识,参见[生命周期钩子](../guide/lifecycle-hooks.html)一章。 学习关于`ngOnChanges()`的更多知识,参见[生命周期钩子](../guide/lifecycle-hooks.html)一章。
:marked :marked
This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes: This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
这个`VersionChildComponent`会监测输入属性`major`和`minor`的变化,并把这些变化编写成日志以报告这些变化。 这个`VersionChildComponent`会监测输入属性`major`和`minor`的变化,并把这些变化编写成日志以报告这些变化。
+makeExample('cb-component-communication/ts/src/app/version-child.component.ts') +makeExample('cb-component-communication/ts/src/app/version-child.component.ts')
:marked :marked
The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them. The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them.
`VersionParentComponent`提供`minor`和`major`值,把修改它们值的方法绑定到按钮上。 `VersionParentComponent`提供`minor`和`major`值,把修改它们值的方法绑定到按钮上。
+makeExample('cb-component-communication/ts/src/app/version-parent.component.ts') +makeExample('cb-component-communication/ts/src/app/version-parent.component.ts')
:marked :marked
Here's the output of a button-pushing sequence: Here's the output of a button-pushing sequence:
下面是点击按钮的结果。 下面是点击按钮的结果。
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges") img(src="/resources/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges")
:marked :marked
### Test it ### Test it
### 测试 ### 测试
Test that ***both*** input properties are set initially and that button clicks trigger Test that ***both*** input properties are set initially and that button clicks trigger
the expected `ngOnChanges` calls and values: the expected `ngOnChanges` calls and values:
测试确保***这两个***输入属性值都被初始化了,当点击按钮后,`ngOnChanges`应该被调用,属性的值也符合预期。 测试确保***这两个***输入属性值都被初始化了,当点击按钮后,`ngOnChanges`应该被调用,属性的值也符合预期。
+makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-onchanges') +makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-onchanges')
:marked :marked
@ -171,36 +204,52 @@ figure.image-display
<a id="child-to-parent"></a> <a id="child-to-parent"></a>
:marked :marked
## Parent listens for child event ## Parent listens for child event
## 父组件监听子组件的事件
## 父组件监听子组件的事件
The child component exposes an `EventEmitter` property with which it `emits`events when something happens. The child component exposes an `EventEmitter` property with which it `emits`events when something happens.
The parent binds to that event property and reacts to those events. The parent binds to that event property and reacts to those events.
子组件暴露一个`EventEmitter`属性,当事件发生时,子组件利用该属性`emits`(向上弹射)事件。父组件绑定到这个事件属性并在事件发生时作出回应。The child's `EventEmitter` property is an ***output property***, 子组件暴露一个`EventEmitter`属性,当事件发生时,子组件利用该属性`emits`(向上弹射)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。
The child's `EventEmitter` property is an ***output property***,
typically adorned with an [@Output decoration](../guide/template-syntax.html#inputs-outputs) typically adorned with an [@Output decoration](../guide/template-syntax.html#inputs-outputs)
as seen in this `VoterComponent`: as seen in this `VoterComponent`:
子组件的`EventEmitter`属性是一个**输出属性**,通常带有[@Output装饰器](../guide/template-syntax.html#inputs-outputs),就像在`VoterComponent`中看到的。 子组件的`EventEmitter`属性是一个**输出属性**,通常带有[@Output装饰器](../guide/template-syntax.html#inputs-outputs),就像在`VoterComponent`中看到的。
+makeExample('cb-component-communication/ts/src/app/voter.component.ts') +makeExample('cb-component-communication/ts/src/app/voter.component.ts')
:marked :marked
Clicking a button triggers emission of a `true` or `false` ,the boolean *payload*. Clicking a button triggers emission of a `true` or `false` ,the boolean *payload*.
点击按钮会触发`true`或`false`(布尔型*有效载荷*)的事件。The parent `VoteTakerComponent` binds an event handler called`onVoted()` that responds to the child event 点击按钮会触发`true`或`false`(布尔型*有效载荷*)的事件。
The parent `VoteTakerComponent` binds an event handler called`onVoted()` that responds to the child event
payload `$event` and updates a counter. payload `$event` and updates a counter.
父组件`VoteTakerComponent`绑定了一个事件处理器(`onVoted`),用来响应子组件的事件(`$event`)并更新一个计数器。
父组件`VoteTakerComponent`绑定了一个事件处理器(`onVoted()`),用来响应子组件的事件(`$event`)并更新一个计数器。
+makeExample('cb-component-communication/ts/src/app/votetaker.component.ts') +makeExample('cb-component-communication/ts/src/app/votetaker.component.ts')
:marked :marked
The framework passes the event argument &mdash; represented by `$event` &mdash; to the handler method, The framework passes the event argument &mdash; represented by `$event` &mdash; to the handler method,
and the method processes it: and the method processes it:
框架(Angular)把事件参数(用`$event`表示)传给事件处理方法,这个方法会处理: 框架(Angular)把事件参数(用`$event`表示)传给事件处理方法,这个方法会处理:
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent") img(src="/resources/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent")
:marked :marked
### Test it ### Test it
### 测试 ### 测试
Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters: Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters:
测试确保点击*Agree*和*Disagree*按钮时,计数器被正确更新。 测试确保点击*Agree*和*Disagree*按钮时,计数器被正确更新。
+makeExample('cb-component-communication/e2e-spec.ts', 'child-to-parent') +makeExample('cb-component-communication/e2e-spec.ts', 'child-to-parent')
:marked :marked
@ -211,12 +260,16 @@ figure.image-display
.l-main-section#parent-to-child-local-var .l-main-section#parent-to-child-local-var
:marked :marked
## Parent interacts with child via *local variable* ## Parent interacts with child via *local variable*
## 父组件与子组件通过*本地变量*互动 ## 父组件与子组件通过*本地变量*互动
A parent component cannot use data binding to read child properties A parent component cannot use data binding to read child properties
or invoke child methods. You can do both or invoke child methods. You can do both
by creating a template reference variable for the child element by creating a template reference variable for the child element
and then reference that variable *within the parent template* and then reference that variable *within the parent template*
as seen in the following example.父组件不能使用数据绑定来读取子组件的属性或调用子组件的方法。但可以在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法,如下例所示。 as seen in the following example.
父组件不能使用数据绑定来读取子组件的属性或调用子组件的方法。但可以在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法,如下例所示。
<a id="countdown-timer-example"></a> <a id="countdown-timer-example"></a>
The following is a child `CountdownTimerComponent` that repeatedly counts down to zero and launches a rocket. It has `start` and `stop` methods that control the clock and it displays a countdown status message in its own template. The following is a child `CountdownTimerComponent` that repeatedly counts down to zero and launches a rocket. It has `start` and `stop` methods that control the clock and it displays a countdown status message in its own template.
@ -224,9 +277,12 @@ figure.image-display
子组件`CountdownTimerComponent`进行倒计时,归零时发射一个导弹。`start`和`stop`方法负责控制时钟并在模板里显示倒计时的状态信息。 子组件`CountdownTimerComponent`进行倒计时,归零时发射一个导弹。`start`和`stop`方法负责控制时钟并在模板里显示倒计时的状态信息。
+makeExample('cb-component-communication/ts/src/app/countdown-timer.component.ts') +makeExample('cb-component-communication/ts/src/app/countdown-timer.component.ts')
:marked :marked
The `CountdownLocalVarParentComponent` that hosts the timer componentis as follows: The `CountdownLocalVarParentComponent` that hosts the timer componentis as follows:
让我们来看看计时器组件的宿主组件`CountdownLocalVarParentComponent`。 让我们来看看计时器组件的宿主组件`CountdownLocalVarParentComponent`。
+makeExample('cb-component-communication/ts/src/app/countdown-parent.component.ts', 'lv') +makeExample('cb-component-communication/ts/src/app/countdown-parent.component.ts', 'lv')
:marked :marked
The parent component cannot data bind to the child's The parent component cannot data bind to the child's
@ -255,16 +311,22 @@ figure.image-display
a(id="countdown-tests") a(id="countdown-tests")
:marked :marked
### Test it ### Test it
### 测试 ### 测试
Test that the seconds displayed in the parent template Test that the seconds displayed in the parent template
match the seconds displayed in the child's status message. match the seconds displayed in the child's status message.
Test also that clicking the *Stop* button pauses the countdown timer: Test also that clicking the *Stop* button pauses the countdown timer:
测试确保在父组件模板中显示的秒数和子组件状态信息里的秒数同步。它还会点击*Stop*按钮来停止倒计时: 测试确保在父组件模板中显示的秒数和子组件状态信息里的秒数同步。它还会点击*Stop*按钮来停止倒计时:
+makeExample('cb-component-communication/e2e-spec.ts', 'countdown-timer-tests') +makeExample('cb-component-communication/e2e-spec.ts', 'countdown-timer-tests')
:marked :marked
[Back to top](#top) [Back to top](#top)
[回到顶部](#top) [回到顶部](#top)
.l-main-section .l-main-section
<a id="parent-to-view-child"></a> <a id="parent-to-view-child"></a>
:marked :marked
@ -299,13 +361,17 @@ a(id="countdown-tests")
.l-sub-section .l-sub-section
:marked :marked
The switch from the *local variable* to the *ViewChild* technique The switch from the *local variable* to the *ViewChild* technique
is solely for the purpose of demonstration.由*本地变量*切换到*ViewChild*技术的唯一目的就是做示范。 is solely for the purpose of demonstration.
由*本地变量*切换到*ViewChild*技术的唯一目的就是做示范。
:marked :marked
Here is the parent, `CountdownViewChildParentComponent`: Here is the parent, `CountdownViewChildParentComponent`:
下面是父组件`CountdownViewChildParentComponent`: 下面是父组件`CountdownViewChildParentComponent`:
+makeExample('cb-component-communication/ts/src/app/countdown-parent.component.ts', 'vc') +makeExample('cb-component-communication/ts/src/app/countdown-parent.component.ts', 'vc')
:marked :marked
It takes a bit more work to get the child view into the parent component *class*. It takes a bit more work to get the child view into the parent component *class*.
@ -350,6 +416,7 @@ a(id="countdown-tests")
### Test it ### Test it
### 测试 ### 测试
Use [the same countdown timer tests](#countdown-tests) as before. Use [the same countdown timer tests](#countdown-tests) as before.
@ -358,11 +425,14 @@ a(id="countdown-tests")
:marked :marked
[Back to top](#top) [Back to top](#top)
[回到顶部]('#top') [回到顶部]('#top')
.l-main-section .l-main-section
<a id="bidirectional-service"></a> <a id="bidirectional-service"></a>
:marked :marked
## Parent and children communicate via a service ## Parent and children communicate via a service
## 父组件和子组件通过服务来通讯 ## 父组件和子组件通过服务来通讯
A parent component and its children share a service whose interface enables bi-directional communication *within the family*. A parent component and its children share a service whose interface enables bi-directional communication *within the family*.
@ -371,7 +441,9 @@ a(id="countdown-tests")
The scope of the service instance is the parent component and its children. The scope of the service instance is the parent component and its children.
Components outside this component subtree have no access to the service or their communications. Components outside this component subtree have no access to the service or their communications.
该服务实例的作用域被限制在父组件和其子组件内。这个组件子树之外的组件将无法访问该服务或者与它们通讯。 该服务实例的作用域被限制在父组件和其子组件内。这个组件子树之外的组件将无法访问该服务或者与它们通讯。
This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children. This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children.
这个`MissionService`把`MissionControlComponent`和多个`AstronautComponent`子组件连接起来。 这个`MissionService`把`MissionControlComponent`和多个`AstronautComponent`子组件连接起来。
@ -380,13 +452,17 @@ a(id="countdown-tests")
:marked :marked
The `MissionControlComponent` both provides the instance of the service that it shares with its children The `MissionControlComponent` both provides the instance of the service that it shares with its children
(through the `providers` metadata array) and injects that instance into itself through its constructor: (through the `providers` metadata array) and injects that instance into itself through its constructor:
`MissionControlComponent`提供服务的实例,并将其共享给它的子组件(通过`providers`元数据数组),子组件可以通过构造函数将该实例注入到自身。 `MissionControlComponent`提供服务的实例,并将其共享给它的子组件(通过`providers`元数据数组),子组件可以通过构造函数将该实例注入到自身。
+makeExample('cb-component-communication/ts/src/app/missioncontrol.component.ts') +makeExample('cb-component-communication/ts/src/app/missioncontrol.component.ts')
:marked :marked
The `AstronautComponent` also injects the service in its constructor. The `AstronautComponent` also injects the service in its constructor.
Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance: Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance:
`AstronautComponent`也通过自己的构造函数注入该服务。由于每个`AstronautComponent`都是`MissionControlComponent`的子组件,所以它们获取到的也是父组件的这个服务实例。 `AstronautComponent`也通过自己的构造函数注入该服务。由于每个`AstronautComponent`都是`MissionControlComponent`的子组件,所以它们获取到的也是父组件的这个服务实例。
+makeExample('cb-component-communication/ts/src/app/astronaut.component.ts') +makeExample('cb-component-communication/ts/src/app/astronaut.component.ts')
.l-sub-section .l-sub-section
@ -408,16 +484,22 @@ a(id="countdown-tests")
The *History* log demonstrates that messages travel in both directions between The *History* log demonstrates that messages travel in both directions between
the parent `MissionControlComponent` and the `AstronautComponent` children, the parent `MissionControlComponent` and the `AstronautComponent` children,
facilitated by the service: facilitated by the service:
*History*日志证明了:在父组件`MissionControlComponent`和子组件`AstronautComponent`之间,信息通过该服务实现了双向传递。 *History*日志证明了:在父组件`MissionControlComponent`和子组件`AstronautComponent`之间,信息通过该服务实现了双向传递。
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service") img(src="/resources/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service")
:marked :marked
### Test it ### Test it
### 测试 ### 测试
Tests click buttons of both the parent `MissionControlComponent` and the `AstronautComponent` children Tests click buttons of both the parent `MissionControlComponent` and the `AstronautComponent` children
and verify that the history meets expectations: and verify that the history meets expectations:
测试确保点击父组件`MissionControlComponent`和子组件`AstronautComponent`两个的组件的按钮时,*History*日志和预期的一样。 测试确保点击父组件`MissionControlComponent`和子组件`AstronautComponent`两个的组件的按钮时,*History*日志和预期的一样。
+makeExample('cb-component-communication/e2e-spec.ts', 'bidirectional-service') +makeExample('cb-component-communication/e2e-spec.ts', 'bidirectional-service')
:marked :marked