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

父组件传值给子组件

1、生成父组件和子组件

  • 子组件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;
}
复制代码

2、经过 setter 截听输入属性值的变化

可使用一个输入属性的 setter,以拦截父组件中值的变化,并作处理。css

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; }
}
复制代码

3、经过ngOnChanges()来截听输入属性值的变化

当须要监视多个、交互式输入属性的时候,本方法比用属性的 setter 更合适。html

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 的视角看来,也就没有什么须要报告的变化了。app

4、父组件监听子组件的事件ui

  • 子组件暴露一个 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++;
  }
}
复制代码

4、父组件读取子组件的属性或调用子组件方法

在父组件中新建一个本地变量来表明子组件,而后利用这个变量来读取子组件的属性和调用子组件的方法。this

// 父组件
<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`;
}
复制代码

5、父组件调用@ViewChild()

若是 父组件的类 须要读取子组件的属性值或调用子组件的方法,就不能使用本地变量方法(本地变量只能在模板中进行)。当父组件类须要这种访问时,能够把子组件做为 ViewChild,注入到父组件里面。spa

// 父组件
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(); }
}
复制代码

6、父组件和子组件经过服务来通信

终极绝招,父组件和它的子组件共享同一个服务,利用该服务在组件家族内部实现双向通信。该服务实例的做用域被限制在父组件和其子组件内。这个组件子树以外的组件将没法访问该服务或者与它们通信。code

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();
  }
}
复制代码
相关文章
相关标签/搜索