雙向綁定 (Two-way Binding)

雙向綁定允許子組件不只接收父組件的數據,還可以將數據的更改傳回父組件。

基本原理

雙向綁定本質上是 Property BindingEvent Binding 的組合:

  • 父組件透過 Property Binding [property] 傳遞值給子組件

  • 子組件透過 Event Binding (event) 將更改後的值回傳給父組件

Angular 提供了特定語法 [(property)] (稱為 "banana in a box") 來簡化這個過程。

傳統實現方式 (裝飾器語法)

雙向綁定遵循強制命名慣例:

  • @Input 屬性名為任意有效名稱

  • @Output 屬性名必須是 @Input 名加上 "Change" 後綴

  @Input({ required: true }) size!: { width: string; height: string };
  @Output() sizeChange = new EventEmitter<{ width: string; height: string }>();
  
  onReset() {
    this.sizeChange.emit({
      width: '200',
      height: '100',
    });
  }

在父組件中使用:

<app-rect [(size)]="rectSize" />

上面的語法等同於:

<app-rect [size]="rectSize" (sizeChange)="rectSize = $event" />

新版實現方式 (Signal 的 model)

從 Angular 17.2+ 開始,可以使用更簡潔的 model 來實現雙向綁定:

  size = model.required<{ width: string; height: string }>();
  
  onReset() {
    this.size.set({
      width: '200',
      height: '100',
    });
  }

在父組件中的使用方式相同:

<app-rect [(size)]="rectSize" />

model 與傳統方式的比較

model 的優勢

  1. 更簡潔的語法 - 不需要單獨定義 @Input@Output

  2. 類型安全 - 輸入和輸出自動共享相同的類型

  3. 一致的 API - 使用與其他 Signal API 一致的 .set() 方法

  4. 更少的錯誤空間 - 避免命名錯誤和忘記定義 Output

使用方式

使用 model 除了 .set() 方法外,還有其他有用的方法:

// 讀取當前值
const currentSize = this.size();

// 更新部分屬性
this.size.update((current) => ({
  ...current,
  width: '300'
}));

// 監聽變化
effect(() => {
  console.log('Size changed:', this.size());
});

Last updated