docs(component-communication): copy edits and update TOCs (#3417)
This commit is contained in:
parent
3ba79f9eb5
commit
9fb57f6b98
|
@ -13,21 +13,14 @@ include ../_util-fns
|
||||||
|
|
||||||
<a id="toc"></a>
|
<a id="toc"></a>
|
||||||
:marked
|
:marked
|
||||||
## Table of contents
|
# Contents
|
||||||
|
|
||||||
[Pass data from parent to child with input binding](#parent-to-child)
|
|
||||||
|
|
||||||
[Intercept input property changes with a setter](#parent-to-child-setter)
|
- [Pass data from parent to child with input binding](#parent-to-child)
|
||||||
|
- [Intercept input property changes with a setter](#parent-to-child-setter)
|
||||||
|
- [Intercept input property changes with `ngOnChanges()`](#parent-to-child-on-changes)
|
||||||
|
- [Parent calls an `@ViewChild()`](#parent-to-view-child)
|
||||||
|
- [Parent and children communicate via a service](#bidirectional-service)
|
||||||
|
|
||||||
[Intercept input property changes with *ngOnChanges*](#parent-to-child-on-changes)
|
|
||||||
|
|
||||||
[Parent listens for child event](#child-to-parent)
|
|
||||||
|
|
||||||
[Parent interacts with child via a *local variable*](#parent-to-child-local-var)
|
|
||||||
|
|
||||||
[Parent calls a *ViewChild*](#parent-to-view-child)
|
|
||||||
|
|
||||||
[Parent and children communicate via a service](#bidirectional-service)
|
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
**See the <live-example name="cb-component-communication"></live-example>**.
|
**See the <live-example name="cb-component-communication"></live-example>**.
|
||||||
|
@ -36,30 +29,30 @@ 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).
|
||||||
|
|
||||||
+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'`.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
+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
|
||||||
|
@ -71,10 +64,10 @@ figure.image-display
|
||||||
## Intercept input property changes with a setter
|
## Intercept input property changes with a 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.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
+makeExample('cb-component-communication/ts/src/app/name-child.component.ts')
|
+makeExample('cb-component-communication/ts/src/app/name-child.component.ts')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
@ -84,12 +77,12 @@ figure.image-display
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src="/resources/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter")
|
img(src="/resources/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter")
|
||||||
|
|
||||||
: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:
|
||||||
|
|
||||||
+makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-setter')
|
+makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-setter')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
@ -98,36 +91,36 @@ figure.image-display
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<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()*
|
||||||
|
|
||||||
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.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
May prefer this approach to the property setter when watching multiple, interacting input properties.
|
You may prefer this approach to the property setter when watching multiple, interacting input properties.
|
||||||
|
|
||||||
Learn about `ngOnChanges` in the [LifeCycle Hooks](../guide/lifecycle-hooks.html) chapter.
|
Learn about `ngOnChanges()` in the [LifeCycle Hooks](../guide/lifecycle-hooks.html) chapter.
|
||||||
: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:
|
||||||
|
|
||||||
+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.
|
||||||
|
|
||||||
+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:
|
||||||
|
|
||||||
+makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-onchanges')
|
+makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-onchanges')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
@ -138,35 +131,35 @@ figure.image-display
|
||||||
: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.
|
||||||
|
|
||||||
The child's `EventEmitter` property is an ***output property***,
|
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`:
|
||||||
|
|
||||||
+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*.
|
||||||
|
|
||||||
The parent `VoteTakerComponent` binds an event handler (`onVoted`) that responds to the child event
|
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.
|
||||||
|
|
||||||
+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 — represented by `$event` — to the handler method,
|
The framework passes the event argument—represented by `$event`—to the handler method,
|
||||||
and the method processes it:
|
and the method processes it:
|
||||||
|
|
||||||
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:
|
||||||
|
|
||||||
+makeExample('cb-component-communication/e2e-spec.ts', 'child-to-parent')
|
+makeExample('cb-component-communication/e2e-spec.ts', 'child-to-parent')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
@ -175,33 +168,33 @@ 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. We 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>
|
||||||
We have a child `CountdownTimerComponent` that repeatedly counts down to zero and launches a rocket.
|
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
|
It has `start` and `stop` methods that control the clock and it displays a
|
||||||
countdown status message in its own template.
|
countdown status message in its own template.
|
||||||
+makeExample('cb-component-communication/ts/src/app/countdown-timer.component.ts')
|
+makeExample('cb-component-communication/ts/src/app/countdown-timer.component.ts')
|
||||||
:marked
|
:marked
|
||||||
Let's see the `CountdownLocalVarParentComponent` that hosts the timer component.
|
The `CountdownLocalVarParentComponent` that hosts the timer component is as follows:
|
||||||
|
|
||||||
+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
|
||||||
`start` and `stop` methods nor to its `seconds` property.
|
`start` and `stop` methods nor to its `seconds` property.
|
||||||
|
|
||||||
We can place a local variable (`#timer`) on the tag (`<countdown-timer>`) representing the child component.
|
You can place a local variable, `#timer`, on the tag `<countdown-timer>` representing the child component.
|
||||||
That gives us a reference to the child component itself and the ability to access
|
That gives you a reference to the child component and the ability to access
|
||||||
*any of its properties or methods* from within the parent template.
|
*any of its properties or methods* from within the parent template.
|
||||||
|
|
||||||
In this example, we wire parent buttons to the child's `start` and `stop` and
|
This example wires parent buttons to the child's `start` and `stop` and
|
||||||
use interpolation to display the child's `seconds` property.
|
uses interpolation to display the child's `seconds` property.
|
||||||
|
|
||||||
Here we see the parent and child working together.
|
Here we see the parent and child working together.
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
|
@ -210,72 +203,73 @@ 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:
|
||||||
|
|
||||||
+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)
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="parent-to-view-child"></a>
|
<a id="parent-to-view-child"></a>
|
||||||
:marked
|
:marked
|
||||||
## Parent calls a *ViewChild*
|
## Parent calls an _@ViewChild()_
|
||||||
|
|
||||||
The *local variable* approach is simple and easy. But it is limited because
|
The *local variable* approach is simple and easy. But it is limited because
|
||||||
the parent-child wiring must be done entirely within the parent template.
|
the parent-child wiring must be done entirely within the parent template.
|
||||||
The parent component *itself* has no access to the child.
|
The parent component *itself* has no access to the child.
|
||||||
|
|
||||||
We can't use the *local variable* technique if an instance of the parent component *class*
|
You can't use the *local variable* technique if an instance of the parent component *class*
|
||||||
must read or write child component values or must call child component methods.
|
must read or write child component values or must call child component methods.
|
||||||
|
|
||||||
When the parent component *class* requires that kind of access,
|
When the parent component *class* requires that kind of access,
|
||||||
we ***inject*** the child component into the parent as a *ViewChild*.
|
***inject*** the child component into the parent as a *ViewChild*.
|
||||||
|
|
||||||
We'll illustrate this technique with the same [Countdown Timer](#countdown-timer-example) example.
|
The following example illustrates this technique with the same
|
||||||
We won't change its appearance or behavior.
|
[Countdown Timer](#countdown-timer-example) example.
|
||||||
|
Neither its appearance nor its behavior will change.
|
||||||
The child [CountdownTimerComponent](#countdown-timer-example) is the same as well.
|
The child [CountdownTimerComponent](#countdown-timer-example) is the same as well.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We are switching from the *local variable* to the *ViewChild* technique
|
The switch from the *local variable* to the *ViewChild* technique
|
||||||
solely for the purpose of demonstration.
|
is solely for the purpose of demonstration.
|
||||||
:marked
|
:marked
|
||||||
Here is the parent, `CountdownViewChildParentComponent`:
|
Here is the parent, `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*.
|
||||||
|
|
||||||
We import references to the `ViewChild` decorator and the `AfterViewInit` lifecycle hook.
|
First, you have to import references to the `ViewChild` decorator and the `AfterViewInit` lifecycle hook.
|
||||||
|
|
||||||
We inject the child `CountdownTimerComponent` into the private `timerComponent` property
|
Next, inject the child `CountdownTimerComponent` into the private `timerComponent` property
|
||||||
via the `@ViewChild` property decoration.
|
via the `@ViewChild` property decoration.
|
||||||
|
|
||||||
The `#timer` local variable is gone from the component metadata.
|
The `#timer` local variable is gone from the component metadata.
|
||||||
Instead we bind the buttons to the parent component's own `start` and `stop` methods and
|
Instead, bind the buttons to the parent component's own `start` and `stop` methods and
|
||||||
present the ticking seconds in an interpolation around the parent component's `seconds` method.
|
present the ticking seconds in an interpolation around the parent component's `seconds` method.
|
||||||
|
|
||||||
These methods access the injected timer component directly.
|
These methods access the injected timer component directly.
|
||||||
|
|
||||||
The `ngAfterViewInit` lifecycle hook is an important wrinkle.
|
The `ngAfterViewInit()` lifecycle hook is an important wrinkle.
|
||||||
The timer component isn't available until *after* Angular displays the parent view.
|
The timer component isn't available until *after* Angular displays the parent view.
|
||||||
So we display `0` seconds initially.
|
So it displays `0` seconds initially.
|
||||||
|
|
||||||
Then Angular calls the `ngAfterViewInit` lifecycle hook at which time it is *too late*
|
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.
|
to update the parent view's display of the countdown seconds.
|
||||||
Angular's unidirectional data flow rule prevents us from updating the parent view's
|
Angular's unidirectional data flow rule prevents updating the parent view's
|
||||||
in the same cycle. We have to *wait one turn* before we can display the seconds.
|
in the same cycle. The app has to *wait one turn* before it can display the seconds.
|
||||||
|
|
||||||
We use `setTimeout` to wait one tick and then revise the `seconds` method so
|
Use `setTimeout()` to wait one tick and then revise the `seconds()` method so
|
||||||
that it takes future values from the timer component.
|
that it takes future values from the timer component.
|
||||||
|
|
||||||
### Test it
|
### Test it
|
||||||
Use [the same countdown timer tests](#countdown-tests) as before.
|
Use [the same countdown timer tests](#countdown-tests) as before.
|
||||||
:marked
|
:marked
|
||||||
[Back to top](#top)
|
[Back to top](#top)
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="bidirectional-service"></a>
|
<a id="bidirectional-service"></a>
|
||||||
:marked
|
:marked
|
||||||
|
@ -284,47 +278,47 @@ a(id="countdown-tests")
|
||||||
A parent component and its children share a service whose interface enables bi-directional communication
|
A parent component and its children share a service whose interface enables bi-directional communication
|
||||||
*within the family*.
|
*within the family*.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
+makeExample('cb-component-communication/ts/src/app/mission.service.ts')
|
+makeExample('cb-component-communication/ts/src/app/mission.service.ts')
|
||||||
: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:
|
||||||
|
|
||||||
+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:
|
||||||
|
|
||||||
+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
|
||||||
:marked
|
:marked
|
||||||
Notice that we capture the `subscription` and unsubscribe when the `AstronautComponent` is destroyed.
|
Notice that this example captures the `subscription` and `unsubscribe()` when the `AstronautComponent` is destroyed.
|
||||||
This is a memory-leak guard step. There is no actual risk in this app because the
|
This is a memory-leak guard step. There is no actual risk in this app because the
|
||||||
lifetime of a `AstronautComponent` is the same as the lifetime of the app itself.
|
lifetime of a `AstronautComponent` is the same as the lifetime of the app itself.
|
||||||
That *would not* always be true in a more complex application.
|
That *would not* always be true in a more complex application.
|
||||||
|
|
||||||
We do not add this guard to the `MissionControlComponent` because, as the parent,
|
You don't add this guard to the `MissionControlComponent` because, as the parent,
|
||||||
it controls the lifetime of the `MissionService`.
|
it controls the lifetime of the `MissionService`.
|
||||||
:marked
|
:marked
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
|
|
||||||
+makeExample('cb-component-communication/e2e-spec.ts', 'bidirectional-service')
|
+makeExample('cb-component-communication/e2e-spec.ts', 'bidirectional-service')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
|
Loading…
Reference in New Issue