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();
  }
}
複製代碼
相關文章
相關標籤/搜索