Common component communication scenarios are listed, in which two or more components share information.
Pass data from parent to child with input binding
HeroChildComponent has two input properties, typically adorned with @Input decorations.
component-interaction/src/app/hero-child.component.ts
import { Component, Input } from ‘@angular/core’;
import { Hero } from ‘./hero’;
@Component({
selector: ‘hero-child’,
template: `
<h3>{{hero.name}} says:</h3>
<p>I, {{hero.name}}, am at your service, {{masterName}}.</p>
`
})
export class HeroChildComponent {
@Input() hero: Hero;
@Input(‘master’) masterName: string;
}
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.
component-interaction/src/app/hero-parent.component.ts
import { Component } from ‘@angular/core’;
import { HEROES } from ‘./hero’;
@Component({
selector: ‘hero-parent’,
template: `
<h2>{{master}} controls {{heroes.length}} heroes</h2>
<hero-child *ngFor=”let hero of heroes”
[hero]=”hero” [master]=”master”></hero-child>
`
})
export class HeroParentComponent {
heroes = HEROES;
master = ‘Master’;
}
E2E test that all children were instantiated and displayed as expected:
component-interaction/e2e-spec.ts
// …
let _heroNames = [‘Mr. IQ’, ‘Magneta’, ‘Bombasto’];
let _masterName = ‘Master’;
it(‘should pass properties to children properly’, function () {
let parent = element.all(by.tagName(‘hero-parent’)).get(0);
let heroes = parent.all(by.tagName(‘hero-child’));
for (let i = 0; i < _heroNames.length; i++) {
let childTitle = heroes.get(i).element(by.tagName(‘h3’)).getText();
let childDetail = heroes.get(i).element(by.tagName(‘p’)).getText();
expect(childTitle).toEqual(_heroNames[i] + ‘ says:’);
expect(childDetail).toContain(_masterName);
}
});
// …
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, typically adorned with an @Output decoration as seen in this VoterComponent:
component-interaction/src/app/voter.component.ts
import { Component, EventEmitter, Input, Output } from ‘@angular/core’;
@Component({
selector: ‘my-voter’,
template: `
<h4>{{name}}</h4>
<button (click)=”vote(true)” [disabled]=”voted”>Agree</button>
<button (click)=”vote(false)” [disabled]=”voted”>Disagree</button>
`
})
export class VoterComponent {
@Input() name: string;
@Output() onVoted = new EventEmitter<boolean>();
voted = false;
vote(agreed: boolean) {
this.onVoted.emit(agreed);
this.voted = true;
}
}
Clicking a button triggers emission of a true or false, the boolean payload.
The parent VoteTakerComponent binds an event handler called onVoted() that responds to the child event payload $event and updates a counter.
component-interaction/src/app/votetaker.component.ts
import { Component } from ‘@angular/core’;
@Component({
selector: ‘vote-taker’,
template: `
<h2>Should mankind colonize the Universe?</h2>
<h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
<my-voter *ngFor=”let voter of voters”
[name]=”voter”(onVoted)=”onVoted($event)”>
</my-voter>
`
})
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = [‘Mr. IQ’, ‘Ms. Universe’, ‘Bombasto’];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
}
}
The framework passes the event argument—represented by $event—to the handler method, and the method processes it
Parent interacts with child via local variable
A parent component cannot use data binding to read child properties or invoke child methods. You can do both by creating a template reference variable for the child element and then reference that variable within the parent template as seen in the following example.
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.
component-interaction/src/app/countdown-timer.component.ts
import { Component, OnDestroy, OnInit } from ‘@angular/core’;
@Component({
selector: ‘countdown-timer’,
template: ‘<p>{{message}}</p>’
})
export class CountdownTimerComponent implements OnInit, OnDestroy {
intervalId = 0;
message = ”;
seconds = 11;
clearTimer() { clearInterval(this.intervalId); }
ngOnInit() { this.start(); }
ngOnDestroy() { this.clearTimer(); }
start() { this.countDown(); }
stop() {
this.clearTimer();
this.message = `Holding at T-${this.seconds} seconds`;
}
private countDown() {
this.clearTimer();
this.intervalId = window.setInterval(() => {
this.seconds -= 1;
if (this.seconds === 0) {
this.message = ‘Blast off!’;
} else {
if (this.seconds < 0) { this.seconds = 10; } // reset
this.message = `T-${this.seconds} seconds and counting`;
}
}, 1000);
}
}
The CountdownLocalVarParentComponent that hosts the timer component is as follows:
component-interaction/src/app/countdown-parent.component.ts
import { Component } from ‘@angular/core’;
import { CountdownTimerComponent } from ‘./countdown-timer.component’;
@Component({
selector: ‘countdown-parent-lv’,
template: `
<h3>Countdown to Liftoff (via local variable)</h3>
<button (click)=”timer.start()”>Start</button>
<button (click)=”timer.stop()”>Stop</button>
<div class=”seconds”>{{timer.seconds}}</div>
<countdown-timer #timer></countdown-timer>
`,
styleUrls: [‘demo.css’]
})
export class CountdownLocalVarParentComponent { }
The parent component cannot data bind to the child’s start and stop methods nor to its seconds property.
You can place a local variable, #timer, on the tag <countdown-timer> representing the child component. 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.
This example wires parent buttons to the child’s start and stop and uses interpolation to display the child’s seconds property.