组件通信
来自官网学习个人总结
父-->子
- 生成一个新组件:
ng generate component product-alerts
命令会自动生成 ProductAlertsComponent 组件并将模块自动添加到AppModule让别的应用都能够直接调用 - 从
@angular/core引入Input. - 定义一个由
@Input()装饰的变量.使子组件可以获取父组件所传变量。
// child component
import { Component, OnInit, Input } from "@angular/core";
import { Product } from "../products";
export class ProductAlertsComponent implements OnInit {
//定义masterName接收自父组件所传入变量master
@Input('master') masterName = '';
//定义product接收自父组件所传入变量product
@Input() product!: Product;
constructor() {}
ngOnInit() {}
}
// parent component
<app-product-alerts
[product]="product"
[master]="master">
</app-product-alerts>
子-->父
- import
OutputandEventEmitterfrom @angular/core. - 在 class 中,以
EventEmitter()实例定义一个被@Output()装饰的方法变量. 这样notify改变时,父组件就能够被通知到
//child - component.ts
export class ProductAlertsComponent {
@Input() product: Product | undefined;
@Output() notify = new EventEmitter();
}
//child - component.html
<p *ngIf="product && product.price > 700">
<button type="button" (click)="notify.emit()">Notify Me</button>
</p>
//parent component.html
<app-product-alerts
[product]="product"
(notify)="onNotify()">
</app-product-alerts>
//parent component.ts
onNotify() {
window.alert('You will be notified');
}
对父组件传来的属性进行setter操作
🌰 对传递的string值空值替换并去除字符串前后空格
最终将展示 'Jcole the', '♥', 'best'
//name-parent.component.ts
content_copyimport { Component } from '@angular/core';
@Component({
selector: 'app-name-parent',
template: `
<h2>Master controls {{names.length}} names</h2>
<app-name-child *ngFor="let name of names" [name]="name"></app-name-child>
`
})
export class NameParentComponent {
names = ['Jcole the', ' ', ' best'];
}
//name.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-name-child',
template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
@Input()
get name(): string { return this._name; }
set name(name: string) {
this._name = (name && name.trim()) || '♥';
}
private _name = '';
}
在ngOnChanges中处理父组件所传属性
ngOnChanges会在ngOnInit之前执行一次,并在每次父组件所传值更新的时候执行
🌰 子组件监听majorandminor的改变并打印在控制台
//version-parent.child.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-version-parent',
template: `
<h2>Source code version</h2>
<button type="button" (click)="newMinor()">New minor version</button>
<button type="button" (click)="newMajor()">New major version</button>
<app-version-child [major]="major" [minor]="minor"></app-version-child>
`
})
export class VersionParentComponent {
major = 1;
minor = 23;
newMinor() {
this.minor++;
}
newMajor() {
this.major++;
this.minor = 0;
}
}
//version-parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-version-parent',
template: `
<h2>Source code version</h2>
<button type="button" (click)="newMinor()">New minor version</button>
<button type="button" (click)="newMajor()">New major version</button>
<app-version-child [major]="major" [minor]="minor"></app-version-child>
`
})
export class VersionParentComponent {
major = 1;
minor = 23;
newMinor() {
this.minor++;
}
newMajor() {
this.major++;
this.minor = 0;
}
}
父组件通过局部变量直接操作子组件内参数
父组件是不可双向绑定子组件数据&直接调用子组件方法的。
有🌰如下:
子组件CountdownTimerComponent为一个倒数组件,能够通过方法start与stop控制倒计时的起始。并展示所倒计时间
//countdown-timer.component.ts
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);
}
}
父组件CountdownLocalVarParentComponent中涵括了倒数器组件CountdownTimerComponent。
并通过两个按钮去控制子组件的起始
在这里我们将本地变量#timer放置在 <countdown-timer>去代表子组件。我们可以通过timmer去操控子组件中的任何变量与函数
//countdown-parent.component.ts
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 type="button" (click)="timer.start()">Start</button>
<button type="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() 函数
刚才局部变量的方法很简单8?
但是父组件本身无权访问子组件,使用时父子连接必须完全在父模板内完成。
组件的父子关系不是在每个组件各自的类中通过局部变量技术建立的。
因为类 实例没有相互连接,所以父类不能访问子类的属性和方法。
如果父组件的类依赖于子组件的类,则不能使用局部变量技术。
我们可以将子组件作为ViewChild 注入到父组件中
以下🌰的子组件CountdownTimerComponent与上🌰保持一致。
我们将父组件从通过局部变量处理改为ViewChild注入处理
//countdown-parent.component.ts
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 type="button" (click)="start()">Start</button>
<button type="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() {
// 重新定义 `seconds()`。与 `CountdownTimerComponent.seconds` 保持一致
// 为避免一次性开发模式直接修改子组件数据 && 单向数据流错误
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
}
父子组件通过service交互
通过service我们也可以实现父子组件的双向通信。
下🌰中MissionService将连接MissionControlComponent到多个AstronautComponent子组件
//mission.service.ts
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);
}
}
//missioncontrol.component.ts
import { Component } from '@angular/core';
import { MissionService } from './mission.service';
@Component({
selector: 'app-mission-control',
template: `
<h2>Mission Control</h2>
<button type="button" (click)="announce()">Announce mission</button>
<app-astronaut
*ngFor="let astronaut of astronauts"
[astronaut]="astronaut">
</app-astronaut>
<h3>History</h3>
<ul>
<li *ngFor="let event of history">{{event}}</li>
</ul>
`,
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() {
const mission = this.missions[this.nextMission++];
this.missionService.announceMission(mission);
this.history.push(`Mission "${mission}" announced`);
if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
}
}
//astronaut.component.ts
import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-astronaut',
template: `
<p>
{{astronaut}}: <strong>{{mission}}</strong>
<button
type="button"
(click)="confirm()"
[disabled]="!announced || confirmed">
Confirm
</button>
</p>
`
})
export class AstronautComponent implements OnDestroy {
@Input() astronaut = '';
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();
}
}