| 
									
										
										
										
											2016-02-05 23:27:06 -08:00
										 |  |  | include ../_util-fns | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | <a id="top"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   This cookbook contains recipes for common component communication scenarios | 
					
						
							|  |  |  |   in which two or more components share information. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  |   .l-sub-section | 
					
						
							|  |  |  |     :marked | 
					
						
							|  |  |  |       For an in-depth look at each fundamental concepts in component communication, we can find detailed description and | 
					
						
							|  |  |  |       samples in the [Component Communication]() document. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <a id="toc"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ## Table of contents | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   [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 listens for child event](#child-to-parent) | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   [Parent interacts with child via a *local variable*](#parent-to-child-local-var) | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |   [Parent calls a *ViewChild*](#parent-to-view-child) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   [Parent and children communicate via a service](#bidirectional-service) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-07-30 21:00:52 -05:00
										 |  |  |   **See the <live-example name="cb-component-communication"></live-example>**. | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | .l-main-section | 
					
						
							|  |  |  | <a id="parent-to-child"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ## Pass data from parent to child with input binding | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   `HeroChildComponent` has two ***input properties***,  | 
					
						
							| 
									
										
										
										
											2016-03-16 20:04:42 -07:00
										 |  |  |   typically adorned with [@Input decorations](../guide/template-syntax.html#inputs-outputs). | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/hero-child.component.ts') | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   The second `@Input` aliases the child component property name `masterName` as `'master'`. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   The `HeroParentComponent` nests the child `HeroChildComponent` inside an `*ngFor` repeater,  | 
					
						
							|  |  |  |   binding its `master` string property to the child's `master` alias | 
					
						
							|  |  |  |   and each iteration's `hero` instance to the child's `hero` property. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/hero-parent.component.ts') | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   The running application displays three heroes: | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | figure.image-display | 
					
						
							|  |  |  |   img(src="/resources/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child") | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ### Test it | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   E2E test that all children were instantiated and displayed as expected: | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-06-01 13:58:01 -07:00
										 |  |  | +makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child') | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   [Back to top](#top) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .l-main-section | 
					
						
							|  |  |  | <a id="parent-to-child-setter"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ## Intercept input property changes with a setter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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`  | 
					
						
							|  |  |  |   trims the whitespace from a name and replaces an empty value with default text.  | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/name-child.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   Here's the `NameParentComponent` demonstrating name variations including a name with all spaces: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/name-parent.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | figure.image-display | 
					
						
							|  |  |  |   img(src="/resources/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter") | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ### Test it | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   E2E tests of input property setter with empty and non-empty names: | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-06-01 13:58:01 -07:00
										 |  |  | +makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-setter') | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   [Back to top](#top) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .l-main-section | 
					
						
							|  |  |  | <a id="parent-to-child-on-changes"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ## 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. | 
					
						
							|  |  |  | .l-sub-section | 
					
						
							|  |  |  |   :marked | 
					
						
							|  |  |  |     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. | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes: | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/version-child.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/version-parent.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   Here's the output of a button-pushing sequence: | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | figure.image-display | 
					
						
							|  |  |  |   img(src="/resources/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges") | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ### Test it | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   Test that ***both*** input properties are set initially and that button clicks trigger  | 
					
						
							|  |  |  |   the expected `ngOnChanges` calls and values: | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-06-01 13:58:01 -07:00
										 |  |  | +makeExample('cb-component-communication/e2e-spec.ts', 'parent-to-child-onchanges') | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   [Back to top](#top) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .l-main-section | 
					
						
							|  |  |  | <a id="child-to-parent"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ## Parent listens for child event | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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 child's `EventEmitter` property is an ***output property***,  | 
					
						
							| 
									
										
										
										
											2016-03-16 20:04:42 -07:00
										 |  |  |     typically adorned with an [@Output decoration](../guide/template-syntax.html#inputs-outputs) | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |     as seen in this `VoterComponent`: | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/voter.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   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 | 
					
						
							|  |  |  |   payload (`$event`) and updates a counter. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/votetaker.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   The framework passes the event argument — represented by `$event` — to the handler method,  | 
					
						
							|  |  |  |   and the method processes it: | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | figure.image-display | 
					
						
							|  |  |  |   img(src="/resources/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent") | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ### Test it | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters: | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-06-01 13:58:01 -07:00
										 |  |  | +makeExample('cb-component-communication/e2e-spec.ts', 'child-to-parent') | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   [Back to top](#top) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-06 10:28:24 -07:00
										 |  |  | .l-main-section#parent-to-child-local-var | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   ## Parent interacts with child via *local variable* | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |    | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   A parent component cannot use data binding to read child properties | 
					
						
							|  |  |  |   or invoke child methods. We can do both  | 
					
						
							| 
									
										
										
										
											2016-04-28 11:18:52 -07:00
										 |  |  |   by creating a template reference variable for the child element | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   and then reference that variable *within the parent template* | 
					
						
							|  |  |  |   as seen in the following example. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   <a id="countdown-timer-example"></a> | 
					
						
							|  |  |  |   We have 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. | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | +makeExample('cb-component-communication/ts/app/countdown-timer.component.ts') | 
					
						
							|  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   Let's see the `CountdownLocalVarParentComponent` that hosts the timer component. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/countdown-parent.component.ts', 'lv') | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   The parent component cannot data bind to the child's  | 
					
						
							|  |  |  |   `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. | 
					
						
							|  |  |  |   That gives us a reference to the child component itself and the ability to access | 
					
						
							|  |  |  |   *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 | 
					
						
							|  |  |  |   use interpolation to display the child's `seconds` property. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   Here we see the parent and child working together. | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | figure.image-display | 
					
						
							|  |  |  |   img(src="/resources/images/cookbooks/component-communication/countdown-timer-anim.gif" alt="countdown timer") | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | a(id="countdown-tests") | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | :marked | 
					
						
							|  |  |  |   ### Test it | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   Test that the seconds displayed in the parent template | 
					
						
							|  |  |  |   match the seconds displayed in the child's status message. | 
					
						
							|  |  |  |   Test also that clicking the *Stop* button pauses the countdown timer: | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |    | 
					
						
							| 
									
										
										
										
											2016-06-01 13:58:01 -07:00
										 |  |  | +makeExample('cb-component-communication/e2e-spec.ts', 'countdown-timer-tests') | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   [Back to top](#top) | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |    | 
					
						
							|  |  |  | .l-main-section | 
					
						
							|  |  |  | <a id="parent-to-view-child"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ## Parent calls a *ViewChild* | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   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 component *itself* has no access to the child. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   We 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. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   When the parent component *class* requires that kind of access,  | 
					
						
							|  |  |  |   we ***inject*** the child component into the parent as a *ViewChild*. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   We'll illustrate this technique with the same [Countdown Timer](#countdown-timer-example) example.  | 
					
						
							|  |  |  |   We won't change its appearance or behavior.  | 
					
						
							|  |  |  |   The child [CountdownTimerComponent](#countdown-timer-example) is the same as well. | 
					
						
							|  |  |  | .l-sub-section | 
					
						
							|  |  |  |   :marked | 
					
						
							|  |  |  |     We are switching from the *local variable* to the *ViewChild* technique | 
					
						
							|  |  |  |     solely for the purpose of demonstration. | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   Here is the parent, `CountdownViewChildParentComponent`: | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/countdown-parent.component.ts', 'vc') | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   It takes a bit more work to get the child view into the parent component classs. | 
					
						
							|  |  |  |     | 
					
						
							|  |  |  |   We import references to the `ViewChild` decorator and the `AfterViewInit` lifecycle hook. | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-05-13 12:37:24 +08:00
										 |  |  |   We inject the child `CountdownTimerComponent` into the private `timerComponent` property | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   via the `@ViewChild` property decoration. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   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 | 
					
						
							|  |  |  |   present the ticking seconds in an interpolation around the parent component's `seconds` method. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   These methods access the injected timer component directly. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   The `ngAfterViewInit` lifecycle hook is an important wrinkle. | 
					
						
							|  |  |  |   The timer component isn't available until *after* Angular displays the parent view. | 
					
						
							|  |  |  |   So we display `0` seconds initially. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   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 us from updating the parent view's | 
					
						
							|  |  |  |   in the same cycle. We have to *wait one turn* before we can display the seconds. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   We use `setTimeout` to wait one tick and then revise the `seconds` method so  | 
					
						
							|  |  |  |   that it takes future values from the timer component. | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-07 11:50:14 -08:00
										 |  |  |   ### Test it | 
					
						
							|  |  |  |   Use [the same countdown timer tests](#countdown-tests) as before. | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   [Back to top](#top) | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | .l-main-section | 
					
						
							|  |  |  | <a id="bidirectional-service"></a> | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ## 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*. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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. | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/mission.service.ts') | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   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: | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/missioncontrol.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-04-10 14:14:51 +03:00
										 |  |  |   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: | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |    | 
					
						
							|  |  |  | +makeExample('cb-component-communication/ts/app/astronaut.component.ts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .l-sub-section | 
					
						
							|  |  |  |   :marked | 
					
						
							|  |  |  |     Notice that we capture 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 | 
					
						
							|  |  |  |     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. | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     We do not add this guard to the `MissionControlComponent` because, as the parent, | 
					
						
							|  |  |  |     it controls the lifetime of the `MissionService`. | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   The *History* log demonstrates that messages travel in both directions between | 
					
						
							| 
									
										
										
										
											2016-04-10 14:14:51 +03:00
										 |  |  |   the parent `MissionControlComponent` and the `AstronautComponent` children, | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |   facilitated by the service: | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | figure.image-display | 
					
						
							|  |  |  |   img(src="/resources/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service") | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   ### Test it | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-04-10 14:14:51 +03:00
										 |  |  |   Tests click buttons of both the parent `MissionControlComponent` and the `AstronautComponent` children | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  |   and verify that the *History* meets expectations: | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2016-06-01 13:58:01 -07:00
										 |  |  | +makeExample('cb-component-communication/e2e-spec.ts', 'bidirectional-service') | 
					
						
							| 
									
										
										
										
											2016-02-02 14:39:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | :marked | 
					
						
							|  |  |  |   [Back to top](#top) |