ControlValueAccessor (CVA)

[Angular] Forms - Control Value Accessor [上] - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

[Angular] Day28. Control Value Accessor (CVA) - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

ControlValueAccessor (CVA) 是什麼?

ControlValueAccessor 是 Angular 中的一個介面,它作為 Angular 表單系統 (FormControl, FormGroup 等) 和 DOM 元素之間的橋樑。簡單來說,它負責:

  1. 將表單控制項的值寫入到 DOM 元素

  2. 從 DOM 元素讀取使用者輸入的值到表單控制項

  3. 處理表單控制項的禁用狀態

為什麼需要實作 ControlValueAccessor?

當你建立自定義表單控制項(如 app-multiple-select)並想要它與 Angular 的表單系統整合時,你需要告訴 Angular 如何:

  • 將值從表單模型寫入你的元件

  • 將使用者在你的元件中的操作轉換回表單模型的值

  • 處理禁用狀態等

[formControl] vs formControlName 的差異

當你使用 [formControl]="someControl" 時,你可以不需要實作 ControlValueAccessor,為什麼?

  • [formControl]="someControl":這是直接將一個 FormControl 實例傳遞給元件,是使用屬性綁定的方式。在這種情況下,你的元件可以直接讀取和操作這個 FormControl 實例,不需要通過 ControlValueAccessor 介面。

  • formControlName="controlName":這是在使用 FormGroup 和 formGroupName 的情境下,你只提供控制項的名稱,而不是控制項本身。在這種情況下,Angular 需要知道如何將表單系統中的值與你的元件連接起來,因此需要 ControlValueAccessor 來做這個轉換。

為什麼普通的 formControl 不需要 CVA?

當你使用 [formControl] 綁定時,你可以直接在元件中訂閱和更新這個控制項:

@Input() formControl: FormControl;

ngOnInit() {
  // 直接訂閱控制項的值變化
  this.formControl.valueChanges.subscribe(newValue => {
    // 處理值變化
  });
}

onChange(newValue) {
  // 直接更新控制項的值
  this.formControl.setValue(newValue);
}

這樣你可以直接操作 FormControl 實例,不需要中間層的 ControlValueAccessor。

但是,當你使用 formControlName 時,你的元件需要實作 ControlValueAccessor 介面,讓 Angular 的表單模組知道如何與你的自定義元件互動:

// 需要實作 ControlValueAccessor 的四個核心方法
writeValue(value: any): void { /* 寫入值到 UI */ }
registerOnChange(fn: any): void { /* 註冊值變化時的回調 */ }
registerOnTouched(fn: any): void { /* 註冊被觸碰時的回調 */ }
setDisabledState(isDisabled: boolean): void { /* 設置禁用狀態 */ }

總結

  • 如果你想讓元件支援 formControlName 指令,就必須實作 ControlValueAccessor 介面

  • 如果你只是想通過 [formControl] 傳遞控制項實例,則無需實作 ControlValueAccessor

  • ControlValueAccessor 允許你的自定義表單控制項無縫整合到 Angular 的表單系統中

Last updated