组件交互
让两个或多个组件之间共享信息的方法
父传子 - 通过输入型绑定把数据从父组件传到子组件
父组件传入: [hero]="hero" [] 中存放在子组件接收时的变量名 = 后存放父组件传入自组建的值。
import { Component } from '@angular/core';
import { HEROES } from './hero';
@Component({
selector: 'app-hero-parent',
template: `
<h2>{{master}} controls {{heroes.length}} heroes</h2>
// 使用子组件是传入值
<app-hero-child
*ngFor="let hero of heroes"
[hero]="hero"
[master]="master">
</app-hero-child>
`
})
export class HeroParentComponent {
heroes = HEROES;
master = 'Master';
}
子组件:heroChildComponent有两个输入型属性,它们通过@Input 装饰器。
需要导入Input
使用@Input() hero 对应 父组件传入的值
使用@Input('master') mastername = ' ' ; 可以起别名 ()内为父组件传递的值
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-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 = '';
}
通过setter截听输入属性值的变化
子组件同样需要导入Input
export class NameChildComponent {
@Input()
get name(): string { return this._name; }
set name(name: string) {
this._name = (name && name.trim()) || '<no name set>';
}
private _name = '';
}
父组件监听子组件的事件
子组件暴露一个
属性,当事件发生时,子组件利用该属性 EventEmitter
emits
(向上弹射)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。
子组件的
属性是一个输出属性,通常带有@Output 装饰器,就像在 EventEmitter
VoterComponent
中看到的。
emit中存放父组件接收事件时的名字,
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-voter',
template: `
<h4>{{name}}</h4>
<button (click)="vote(true)" [disabled]="didVote">Agree</button>
<button (click)="vote(false)" [disabled]="didVote">Disagree</button>
`
})
export class VoterComponent {
@Input() name = '';
@Output() voted = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.voted.emit(agreed);
this.didVote = true;
}
}
父组件:
绑定了一个事件处理器(onVoted()
),用来响应子组件的事件($event
)并更新一个计数器。
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 = ['Narco', 'Celeritas', 'Bombasto'];
onVoted(agreed: boolean) {
if (agreed) {
this.agreed++;
} else {
this.disagreed++;
}
}
}
父组件监听子组件的事件
子组件暴露一个EventEmitter
属性,当事件发生时,子组件利用该属性 emits
(向上弹射)事件。父组件绑定到这个属性,并在发生时响应。
子组件的 EventEmitter
属性是一个输出属性,通常带有@Output 装饰器,就像在 VoterComponent
中看到的。
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-voter',
template: `
<h4>{{name}}</h4>
<button (click)="vote(true)" [disabled]="didVote">Agree</button>
<button (click)="vote(false)" [disabled]="didVote">Disagree</button>
`
})
export class VoterComponent {
@Input() name = '';
@Output() voted = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.voted.emit(agreed);
this.didVote = true;
}
}
父组件 VoteTakerComponent
绑定了一个事件处理器(onVoted()
),用来响应子组件的事件($event
)并更新一个计数器。
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 = ['Narco', 'Celeritas', 'Bombasto'];
onVoted(agreed: boolean) {
if (agreed) {
this.agreed++;
} else {
this.disagreed++;
}
}
}
父组件与子组件通过本地变量互动
主要实现思路: 在父组件模板中使用子组件时给子组件添加一个别名,用于调用子组件中的方法。也就是在父组件中新建一个本地变量来代表子组件,通过这个变量来进行对子组件的属性和方法进行调用。
子组件:
import { Component, OnDestroy } from '@angular/core';
@Component({
selector: 'app-countdown-timer',
template: '<p>{{message}}</p>'
})
export class CountdownTimerComponent implements OnDestroy {
intervalId = 0;
message = '';
seconds = 11;
ngOnDestroy() { this.clearTimer(); }
start() { this.countDown(); }
stop() {
this.clearTimer();
this.message = `Holding at T-${this.seconds} seconds`;
}
private clearTimer() { clearInterval(this.intervalId); }
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: `
<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>
<app-countdown-timer #timer></app-countdown-timer>
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownLocalVarParentComponent { }
父组件调用@ViewChild()
本地变量的方法简单,但是他只可以在模板中使用,而父组件本身的代码对于子组件没有访问权限。
组件之间的父子关系,不能通过在每个组件的类中各自定义的本地变量来定义,因为这两个类的实例互相不知道,因此父类也就不能访问子类中的属性和方法。
而如果想实现在类中访问子组件的属性和方法,可以把子组件作为ViewChild,注入到父组件中。
在上面的本地变量的方法中只需要更改,父组件即可,父组件中需要导入对装饰器 ViewChild
以及生命周期钩子 AfterViewInit
的引用
import { AfterViewInit, ViewChild } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
export class CountdownViewChildParentComponent implements AfterViewInit {
//注入子组件,之后可以直接使用子组件中的属性和方法
@ViewChild(CountdownTimerComponent)
//!是检测!后面为null或undefined 不会抛出异常
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(); }
}