父子组件通信
在真实的应用中,组件最终会构成树形结构,就像人类社会中的家族树一样:

在树形结构里面,组件之间有几种典型的关系:父子关系、兄弟关系、没有直接关系。
相应地,组件之间有以下几种典型的通讯方案:
-
直接的父子关系:父组件直接访问子组件的 public 属性和方法。
-
直接的父子关系:借助于 @Input 和 @Output 进行通讯
-
没有直接关系:借助于 Service 单例进行通讯。
-
利用 cookie 和 localstorage 进行通讯。
-
利用 session 进行通讯。
父子通信:Input Down
参考文档:https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding
-
父组件通过子组件标签传递属性
-
子组件在内部声明
@Input接收
-
Input 是单向的
-
父组件如果把数据改了,子组件也会更新
-
但是反之不会
-
有一个例外,引用类型修改
-
下面是一个示例:
子组件:
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@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: string; // 接收 master 重命名为 masterName
}
父组件:
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';
}
父子通信:Output Up
参考文档:https://angular.io/guide/component-interaction#parent-listens-for-child-event
@Output 的本质是事件机制,我们可以利用它来订阅子组件上发布的事件,子组件上这样写:
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-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;
}
}
在父组件中订阅处理:
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"
(onVoted)="onVoted($event)">
</app-voter>
<!-- $event在这里是自定义事件对象,接收到的是子组件内部发布事件传递的数据 -->
`
})
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
onVoted(agreed: boolean) {
agreed ? this.agreed++ : this.disagreed++;
}
}
参考文档:https://angular.io/guide/component-interaction#parent-interacts-with-child-via-local-variable
<app-foo #child></app-foo> <button (click)="child.increment()">调用子组件的方法</button>
显然,子组件里面必须暴露一个 public 型的 childFn 方法,就像这样:
export class FooComponent implements OnInit { public message: string = 'foo message' public count: number = 0 constructor() { } public increment (): void { this.count++ } ngOnInit() { } }
以上是通过在模板里面定义局部变量的方式来直接调用子组件里面的 public 型方法。在父组件的内部也可以访问到子组件的实例,需要利用到 @ViewChild 装饰器,示例如下:
@ViewChild(ChildComponent) private childComponent: ChildComponent;
关于 @ViewChild 在后面的内容里面会有更详细的解释。
没有直接关系通信:Service 单例
参考文档:https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

小结
组件间的通讯方案是通用的,无论你使用什么样的前端框架,都会面临这个问题,而解决的方案无外乎本文所列出的几种。



浙公网安备 33010602011771号