父组件传值给子组件
一、生成父组件和子组件
- 子组件HeroChildComponent中两个输入型属性hero和masterName,通常带@Input修饰符
- 第二个 @Input 为子组件的属性名 masterName 指定一个别名 master(不推荐为起别名,请参见风格指南).
- hero-parent.component.html
<app-hero-child *ngFor="let hero of heroes"
[hero]="hero"
[master]="master">
</app-hero-child>
复制代码
- hero-child.component.ts
export class HeroChildComponent {
@Input() hero: Hero;
@Input('master') masterName: string;
}
复制代码
二、通过 setter 截听输入属性值的变化
可以使用一个输入属性的 setter,以拦截父组件中值的变化,并做处理。
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-name-child',
template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
private _name = '';
@Input()
set name(name: string) {
// trim掉name的空格,若为空则替换成默认字符串
this._name = (name && name.trim()) || '<no name set>';
}
get name(): string { return this._name; }
}
复制代码
三、通过ngOnChanges()来截听输入属性值的变化
当需要监视多个、交互式输入属性的时候,本方法比用属性的 setter 更合适。
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(', '));
}
复制代码
备注:若输入类型是对象等引入类型数据。 Angular 不会关注这个引入类型的某个属性的变化。 只要引用没有发生变化,于是从 Angular 的视角看来,也就没有什么需要报告的变化了。
四、父组件监听子组件的事件
- 子组件暴露一个 EventEmitter 属性,当事件发生时,子组件利用该属性 emits(向上弹射)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。
- 子组件的 EventEmitter 属性是一个输出属性,带有@Output 装饰器。
// 子组件
export class VoterComponent {
@Input() name: string;
@Output() voted = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.voted.emit(agreed);
this.didVote = true;
}
}
复制代码
// 父组件
import { Component } from '@angular/core';
@Component({
selector: 'app-vote-taker',
template: `
<h2>Should mankind colonize the Universe?</h2>
<h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
<app-voter *ngFor="let voter of voters"
[name]="voter"
(voted)="onVoted($event)">
</app-voter>
`
})
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
}
}
复制代码
四、父组件读取子组件的属性或调用子组件方法
在父组件中新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法。
// 父组件
<button (click)="timer.start()">Start</button>
<button (click)="timer.stop()">Stop</button>
<app-countdown-timer #timer></app-countdown-timer>
复制代码
// 子组件
start() { this.countDown(); }
stop() {
this.clearTimer();
this.message = `Holding at T-${this.seconds} seconds`;
}
复制代码
五、父组件调用@ViewChild()
如果 父组件的类 需要读取子组件的属性值或调用子组件的方法,就不能使用本地变量方法(本地变量只能在模板中进行)。当父组件类需要这种访问时,可以把子组件作为 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: `
<h3>Countdown to Liftoff (via ViewChild)</h3>
<button (click)="start()">Start</button>
<button (click)="stop()">Stop</button>
<div class="seconds">{{ seconds() }}</div>
<app-countdown-timer></app-countdown-timer>
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;
seconds() { return 0; }
ngAfterViewInit() {
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
}
复制代码
六、父组件和子组件通过服务来通讯
终极绝招,父组件和它的子组件共享同一个服务,利用该服务在组件家族内部实现双向通讯。该服务实例的作用域被限制在父组件和其子组件内。这个组件子树之外的组件将无法访问该服务或者与它们通讯。
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class MissionService {
// Observable string sources
private missionAnnouncedSource = new Subject<string>();
private missionConfirmedSource = new Subject<string>();
// 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';
export class AstronautComponent implements OnDestroy {
@Input() astronaut: string;
mission = '<no mission announced>';
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();
}
}
复制代码