Angular学习(三):组件-组件间的通信

父组件传值给子组件

一、生成父组件和子组件
  • 子组件HeroChildComponent中两个输入型属性hero和masterName,通常带@Input修饰符
  • 第二个 @Input 为子组件的属性名 masterName 指定一个别名 master(不推荐为起别名,请参见风格指南).
  1. hero-parent.component.html
<app-hero-child *ngFor="let hero of heroes"
  [hero]="hero"
  [master]="master">
</app-hero-child>
复制代码
  1. 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();
  }
}
复制代码

转载于:https://juejin.im/post/5cd3e63af265da035f6ff183

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值