1. 程式人生 > 實用技巧 >父元件監聽子元件的事件

父元件監聽子元件的事件

子元件暴露一個EventEmitter屬性,當事件發生時,子元件利用該屬性emits(向上彈射)事件。父元件繫結到這個事件屬性,並在事件發生時作出迴應。

子元件的EventEmitter屬性是一個輸出屬性,通常帶有@Output 裝飾器,就像在VoterComponent中看到的。

component-interaction/src/app/voter.component.ts

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-voter',
  template: `
    <h4>{{name}}</h4>
    <button (click)="vote(true)"  [disabled]="didVote">Agree</button>
    <button (click)="vote(false)" [disabled]="didVote">Disagree</button>
  `
})
export class VoterComponent {
  @
Input
() name: string; @Output() voted = new EventEmitter<boolean>(); didVote = false; vote(agreed: boolean) { this.voted.emit(agreed); this.didVote = true; } }

點選按鈕會觸發truefalse(布林型有效載荷)的事件。

父元件VoteTakerComponent綁定了一個事件處理器(onVoted()),用來響應子元件的事件($event)並更新一個計數器。

component-interaction/src/app/votetaker.component.ts

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 = ['Narco', 'Celeritas', 'Bombasto'];

  onVoted(agreed: boolean) {
    agreed ? this.agreed++ : this.disagreed++;
  }
}

測試一下!

測試確保點選Agree和Disagree按鈕時,計數器被正確更新。

component-interaction/e2e/src/app.e2e-spec.ts
// ...
it('should not emit the event initially', function () {
  let voteLabel = element(by.tagName('app-vote-taker'))
    .element(by.tagName('h3')).getText();
  expect(voteLabel).toBe('Agree: 0, Disagree: 0');
});

it('should process Agree vote', function () {
  let agreeButton1 = element.all(by.tagName('app-voter')).get(0)
    .all(by.tagName('button')).get(0);
  agreeButton1.click().then(function() {
    let voteLabel = element(by.tagName('app-vote-taker'))
      .element(by.tagName('h3')).getText();
    expect(voteLabel).toBe('Agree: 1, Disagree: 0');
  });
});

it('should process Disagree vote', function () {
  let agreeButton1 = element.all(by.tagName('app-voter')).get(1)
    .all(by.tagName('button')).get(1);
  agreeButton1.click().then(function() {
    let voteLabel = element(by.tagName('app-vote-taker'))
      .element(by.tagName('h3')).getText();
    expect(voteLabel).toBe('Agree: 1, Disagree: 1');
  });
});
// ...