@Output() 和 EventEmitter

概述

在 Angular 中,@Output() 裝飾器和 EventEmitter 類別是實現子組件向父組件傳遞資料的主要機制。這個機制類似於 Vue 中的 $emit

基本用法

  1. 在子組件中定義輸出的事件:

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

@Component({
  selector: 'app-child',
  styles: `.btn { padding: 5px; }`,
  template: `
    <button class="btn" (click)="addItem()">Add Item</button>
  `,
  standalone: true,
})
export class ChildComponent {
    // 定義一個輸出事件,型別是 EventEmitter<string>
    @Output() addItemEvent = new EventEmitter<string>()
    
    // 當按鈕被點擊時調用此方法
    addItem() {
        // 發射一個烏龜表情符號事件
        this.addItemEvent.emit('🐢');
  }
}
  1. 在父組件中接收事件:

import {Component} from '@angular/core';
import {ChildComponent} from './child.component';

@Component({
  selector: 'app-root',
  template: `
    <!-- 
    使用子組件 app-child
    (addItemEvent) 監聽子組件發出的事件
    $event 是子組件發出的數據,這裡是 🐢
    addItem() 是處理事件的方法
    -->
    <app-child (addItemEvent)="addItem($event)" />
    <p>🐢 all the way down {{ items.join(' ') }}</p>
  `,
  standalone: true,
  imports: [ChildComponent],
})

export class AppComponent {
  items: string[] = [];
  
  // 定義添加項目的方法
  // 參數 item 是從子組件接收到的字串(🐢表情符號)
  addItem(item: string) {
    this.items.push(item);
  }
}

以下 AI 提供

EventEmitter 詳解

建立 EventEmitter

typescriptCopy// 指定發射值的類型
@Output() numberEvent = new EventEmitter<number>();
@Output() objectEvent = new EventEmitter<{id: number, name: string}>();
@Output() anyEvent = new EventEmitter<any>();  // 不建議使用 any

EventEmitter 的主要方法

  • emit(value): 發射一個事件

  • subscribe(): 訂閱事件

  • unsubscribe(): 取消訂閱

  • pipe(): 用於串接 RxJS 操作符

最佳實踐

  1. 命名慣例

// 建議的命名方式
@Output() valueChange = new EventEmitter<string>();  // 值改變
@Output() submit = new EventEmitter<void>();         // 動作
@Output() statusUpdate = new EventEmitter<Status>(); // 狀態更新
  1. 型別安全

// 定義介面
interface UserData {
  id: number;
  name: string;
}

// 使用介面作為型別
@Output() userSelect = new EventEmitter<UserData>();
  1. 事件處理

@Component({
  template: `
    <!-- 直接處理 -->
    <app-child (dataEvent)="handleData($event)"></app-child>

    <!-- 使用範本參考變數 -->
    <app-child (dataEvent)="data = $event" #child></app-child>
    <p>{{ data }}</p>
  `
})

進階用法

  1. 多值發射

@Output() multiData = new EventEmitter<{name: string, age: number}>();

sendMultiData() {
  this.multiData.emit({
    name: 'John',
    age: 30
  });
}
  1. 事件別名

@Output('customName') originalName = new EventEmitter<string>();
  1. 與 RxJS 整合

@Output() searchEvent = new EventEmitter<string>();

// 在父組件中
<app-search (searchEvent)="onSearch($event)"></app-search>

onSearch(term: string) {
  this.searchEvent
    .pipe(
      debounceTime(300),
      distinctUntilChanged()
    )
    .subscribe(value => {
      // 處理搜尋邏輯
    });
}

與 Vue $emit 的對比

Angular:

@Output() userEvent = new EventEmitter<string>();
this.userEvent.emit('data');

Vue:

this.$emit('user-event', 'data');

主要差異:

  • Angular 需要明確宣告和型別定義

  • Angular 使用 EventEmitter 類別

  • Vue 直接使用 $emit 方法

  • Angular 提供更多型別安全性

Last updated