Loading

父子组件通信

组件通信

参考官方文档:https://angular.io/guide/component-interaction

组件就像零散的积木,我们需要把这些积木按照一定的规则拼装起来,而且要让它们互相之间能进行通讯,这样才能构成一个有机的完整系统。

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

在树形结构里面,组件之间有几种典型的关系:父子关系、兄弟关系、没有直接关系。

相应地,组件之间有以下几种典型的通讯方案:

  • 直接的父子关系:父组件直接访问子组件的 public 属性和方法。

  • 直接的父子关系:借助于 @Input 和 @Output 进行通讯

  • 没有直接关系:借助于 Service 单例进行通讯。

  • 利用 cookie 和 localstorage 进行通讯。

  • 利用 session 进行通讯。

无论你使用什么前端框架,组件之间的通讯都离开不以上几种方案,这些方案与具体框架无关。

父子通信:Input Down

参考文档:https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding

  1. 父组件通过子组件标签传递属性

  2. 子组件在内部声明 @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++;
  }
}

父子通信:父组件直接访问子组件 public 成员

参考文档:https://angular.io/guide/component-interaction#parent-interacts-with-child-via-local-variable

对于有直接父子关系的组件,父组件可以直接访问子组件里面 public 型的属性和方法,示例代码片段如下:

<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

 

利用 Cookie 和 localStorage 进行通信

利用 Session 进行通信

小结

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

posted @ 2022-03-19 13:57  1640808365  阅读(170)  评论(0编辑  收藏  举报