錯誤百科

表單欄位上不支援的繫結

範本在元素上使用 [formField] 指令時,繫結或以靜態屬性設定的某個屬性,已經由訊號表單所控制。 欄位狀態是 min、max、required、disabled 與 readonly 這類屬性的單一事實來源。在範本中指定它們,會與欄位的結構定義邏輯衝突。

錯誤訊息會指出該繫結,內容為 "Binding to '[min]' is not allowed..." 或 "Setting the 'required' attribute is not allowed..."。

import {Component, signal} from '@angular/core';
import {form, FormField} from '@angular/forms/signals';

@Component({
  selector: 'app-root',
  imports: [FormField],
  template: `
    <!-- NG8022: [min] and [max] are not allowed with [formField]. -->
    <input type="number" [formField]="f.age" [min]="18" [max]="99" />

    <!-- NG8022: the required attribute is not allowed with [formField]. -->
    <input type="text" [formField]="f.name" required />
  `,
})
export class App {
  f = form(signal({age: 20, name: ''}));
}

當範本在自訂表單控制項上繫結了欄位所管理的輸入時(例如在宣告了 max 輸入的控制項上使用 [max]),同樣會發生這個錯誤。

不能與 [formField] 併用的屬性包括:

  • 值: value、checked(由底層的模型訊號直接管理)
  • 驗證規則: required、min、max、minLength、maxLength、pattern(以及原生 HTML 的 minlength 與 maxlength)
  • 互動狀態: disabled、readonly、hidden
  • 欄位狀態與中繼資料: name、dirty、touched、invalid、errors、pending、disabledReasons(在自訂控制項上,FormField 會自動把這些狀態傳給控制項,所以手動繫結它們會與框架的自動接線衝突)

原生元素上有少數例外:

  • min 與 max 可用於 date、time、month、week 與 datetime-local 類型的 <input> 元素。在這些類型上,限制是像 2026-01-01 這樣的日期或時間字串,數值型的 min() 與 max() 規則無法表達。
  • 當 type 是動態繫結時,min 與 max 也可用,因為編譯器無法知道 input 的類型。
  • value 可用於 <input type="radio">,因為在那裡它用來識別選項。由欄位值決定哪個 radio 按鈕被選取。

除錯方式

從範本中移除該繫結,再依其類別處理該屬性:

  • 值(value、checked): [formField] 會自動把元素與底層的模型訊號繫結並雙向同步。若要初始化或變更值,請更新傳給 form() 的模型訊號。
  • 驗證規則(required、min、max、maxLength 等): 在 form() 的結構定義函式中使用對應的驗證規則來宣告。
  • 互動狀態(disabled、readonly、hidden): 使用結構定義規則 disabled()、readonly() 與 hidden(),它們也接受選用的 when 條件,因此規則可以取決於其他欄位。請見表單邏輯。
  • 欄位狀態與中繼資料(dirty、touched、errors 等): 不需要替代寫法。[formField] 會從欄位狀態套用它們,你也可以從欄位讀取,例如 f.age().dirty()。

上述範例改寫後如下:

import {Component, signal} from '@angular/core';
import {disabled, form, FormField, max, maxLength, min, required} from '@angular/forms/signals';

@Component({
  selector: 'app-root',
  imports: [FormField],
  template: `
    <input type="number" [formField]="f.age" />
    <input type="text" [formField]="f.name" />
  `,
})
export class App {
  f = form(signal({age: 20, name: ''}), (p) => {
    min(p.age, 18);
    max(p.age, 99);
    required(p.name);
    maxLength(p.name, 20);
    disabled(p.name, {when: ({valueOf}) => valueOf(p.age) < 18});
  });
}