组件交互的几种方式
- 通过输入型绑定把数据从父组件传到子组件
- 通过setter截听输入属性值的变化
- 通过ngOnChanges()来截听输入属性值的变化
- 父组件监听子组件的事件
- 父组件与子组件通过本地变量互通
- 父组件调用@viewChild()
- 父组件和子组件通过服务来通讯
@Input()
//子组件
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'app-hero-child',
template: `
{{hero.name}} says:
I, {{hero.name}}, am at your service, {{masterName}}.
`
})
export class HeroChildComponent {
@Input() hero: Hero;
@Input('master') masterName: string;
}
//父组件
import { Component } from '@angular/core';
import { HEROES } from './hero';
@Component({
selector: 'app-hero-parent',
template: `
{{master}} controls {{heroes.length}} heroes
`
})
export class HeroParentComponent {
heroes = HEROES;
master = 'Master';
}
setter
//子组件
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-name-child',
template: '"{{name}}"
'
})
export class NameChildComponent {
private _name = '';
@Input()
set name(name: string) {
this._name = (name && name.trim()) || '';
}
get name(): string { return this._name; }
}
//父组件
import { Component } from '@angular/core';
@Component({
selector: 'app-name-parent',
template: `
Master controls {{names.length}} names
`
})
export class NameParentComponent {
// Displays 'Mr. IQ', '', 'Bombasto'
names = ['Mr. IQ', ' ', ' Bombasto '];
}
ngOnChanges()
//子组件
import { Component, Input, OnChanges, SimpleChange } from '@angular/core';
@Component({
selector: 'app-version-child',
template: `
Version {{major}}.{{minor}}
Change log:
- {{change}}
`
})
export class VersionChildComponent implements OnChanges {
@Input() major: number;
@Input() minor: number;
changeLog: string[] = [];
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
let log: string[] = [];
for (let propName in changes) {
let changedProp = changes[propName];
let to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) {
log.push(`Initial value of ${propName} set to ${to}`);
} else {
let from = JSON.stringify(changedProp.previousValue);
log.push(`${propName} changed from ${from} to ${to}`);
}
}
this.changeLog.push(log.join(', '));
}
}
//父组件
import { Component } from '@angular/core';
@Component({
selector: 'app-version-parent',
template: `
Source code version
`
})
export class VersionParentComponent {
major = 1;
minor = 23;
newMinor() {
this.minor++;
}
newMajor() {
this.major++;
this.minor = 0;
}
}
EventEmitter
//子组件
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-voter',
template: `
{{name}}
`
})
export class VoterComponent {
@Input() name: string;
@Output() onVoted = new EventEmitter();
voted = false;
vote(agreed: boolean) {
this.onVoted.emit(agreed);
this.voted = true;
}
}
//父组件
import { Component } from '@angular/core';
@Component({
selector: 'app-vote-taker',
template: `
Should mankind colonize the Universe?
Agree: {{agreed}}, Disagree: {{disagreed}}
`
})
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
}
}
本地变量
//子组件
import { Component, OnDestroy, OnInit } from '@angular/core';
@Component({
selector: 'app-countdown-timer',
template: '{{message}}
'
})
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);
}
}
//父组件
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
@Component({
selector: 'app-countdown-parent-lv',
template: `
Countdown to Liftoff (via local variable)
{{timer.seconds}}
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownLocalVarParentComponent { }
@ViewChild()
//父组件
import { AfterViewInit, ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
@Component({
selector: 'app-countdown-parent-vc',
template: `
Countdown to Liftoff (via ViewChild)
{{ seconds() }}
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;
seconds() { return 0; }
ngAfterViewInit() {
// Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ...
// but wait a tick first to avoid one-time devMode
// unidirectional-data-flow-violation error
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
}
服务
//服务
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class MissionService {
// Observable string sources
private missionAnnouncedSource = new Subject();
private missionConfirmedSource = new Subject();
// Observable string streams
missionAnnounced$ = this.missionAnnouncedSource.asObservable();
missionConfirmed$ = this.missionConfirmedSource.asObservable();
// Service message commands
announceMission(mission: string) {
this.missionAnnouncedSource.next(mission);
}
confirmMission(astronaut: string) {
this.missionConfirmedSource.next(astronaut);
}
}
//子组件
import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'app-astronaut',
template: `
{{astronaut}}: {{mission}}
`
})
export class AstronautComponent implements OnDestroy {
@Input() astronaut: string;
mission = '';
confirmed = false;
announced = false;
subscription: Subscription;
constructor(private missionService: MissionService) {
this.subscription = missionService.missionAnnounced$.subscribe(
mission => {
this.mission = mission;
this.announced = true;
this.confirmed = false;
});
}
confirm() {
this.confirmed = true;
this.missionService.confirmMission(this.astronaut);
}
ngOnDestroy() {
// prevent memory leak when component destroyed
this.subscription.unsubscribe();
}
}
//父组件
import { Component } from '@angular/core';
import { MissionService } from './mission.service';
@Component({
selector: 'app-mission-control',
template: `
Mission Control
History
- {{event}}
`,
providers: [MissionService]
})
export class MissionControlComponent {
astronauts = ['Lovell', 'Swigert', 'Haise'];
history: string[] = [];
missions = ['Fly to the moon!',
'Fly to mars!',
'Fly to Vegas!'];
nextMission = 0;
constructor(private missionService: MissionService) {
missionService.missionConfirmed$.subscribe(
astronaut => {
this.history.push(`${astronaut} confirmed the mission`);
});
}
announce() {
let mission = this.missions[this.nextMission++];
this.missionService.announceMission(mission);
this.history.push(`Mission "${mission}" announced`);
if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
}
}