Template-driven Form 表單驗證

內建驗證屬性

Angular 提供多種內建的驗證屬性,可以直接加在表單元素上:

<input id="email" type="email" name="email" ngModel required email />

常用的驗證屬性包括:

  • required - 必填欄位

  • email - 必須是有效的電子郵件格式

  • minlength="數字" - 最小長度

  • maxlength="數字" - 最大長度

  • pattern="正則表達式" - 符合指定的正則表達式模式

驗證的工作方式

重要的是,這些驗證屬性不會阻止表單提交,它們只會改變表單的內部狀態,例如將 status 變成 INVALID

因此,我們需要在提交方法中手動檢查表單的有效性:

onSubmit(formData: NgForm) {
  if (formData.form.invalid) {
    return;  // 如果表單無效,則不處理提交
  }
  
  const enteredEmail = formData.form.value.email;
  const enteredPassword = formData.form.value.password;
  console.log(enteredEmail, enteredPassword);
}

表單控制項的狀態

每個表單控制項都有以下狀態:

  1. valid/invalid:控制項值是否通過所有驗證規則

  2. touched/untouched:使用者是否與元素互動後離開(如點擊後失焦)

  3. dirty/pristine:使用者是否修改過元素的值

這些狀態可用於判斷何時顯示錯誤訊息,以提供更好的使用者體驗。

存取表單控制項

方法一:使用表單物件

可以透過表單物件存取控制項及其狀態:

@if (form.form.controls['email'].touched && 
     form.form.controls['email'].invalid) {
  <p class="control-error">Invalid email address.</p>
}

但這種方法有個問題:在表單初始化前,controls 可能未定義,導致錯誤:

ERROR TypeError: Cannot read properties of undefined (reading 'touched')

方法二:使用控制項參考(推薦)

更好的方法是使用模板變數直接引用控制項:

<input
  id="email"
  type="email"
  name="email"
  ngModel
  required
  email
  #emailCtrl="ngModel"
/>

#emailCtrl="ngModel" 告訴 Angular 將這個輸入元素的 NgModel 實例存儲在 emailCtrl 變數中。

然後可以使用這個變數來檢查控制項的狀態:

@if (emailCtrl.touched && emailCtrl.invalid) {
  <p class="control-error">Invalid email address.</p>
}

根據情境顯示特定錯誤

通常我們會為每個欄位顯示特定的錯誤訊息:

@if (emailCtrl.touched && emailCtrl.dirty && emailCtrl.invalid) {
  <p class="control-error">Invalid email address entered.</p>
} 

@if (passwordCtrl.touched && passwordCtrl.dirty && passwordCtrl.invalid) {
  <p class="control-error">
    Invalid password entered - must be at least 6 characters long.
  </p>
}

或者可以更進一步,針對特定的錯誤類型顯示不同訊息:

@if (emailCtrl.touched && emailCtrl.invalid) {
  @if (emailCtrl.errors?.['required']) {
    <p class="control-error">Email is required.</p>
  } @else if (emailCtrl.errors?.['email']) {
    <p class="control-error">Please enter a valid email address.</p>
  }
}

表單狀態的 CSS 類別

Angular 會自動為表單控制項添加 CSS 類別,反映其狀態:

  • ng-valid / ng-invalid - 控制項是否有效

  • ng-touched / ng-untouched - 控制項是否被觸摸過

  • ng-dirty / ng-pristine - 控制項值是否被修改過

這些類別可以用於樣式設計:

input.ng-invalid.ng-touched {
  border: 1px solid red;
  background-color: #ffdddd;
}

input.ng-valid.ng-touched {
  border: 1px solid green;
  background-color: #ddffdd;
}

提交按鈕狀態控制

可以根據表單的有效性控制提交按鈕的狀態:

<button type="submit" [disabled]="form.invalid">Submit</button>

這樣當表單無效時,提交按鈕將被禁用。

常見使用場景和最佳實踐

  1. 不要過早顯示錯誤

    • 通常在使用者與欄位互動後(touched)才顯示錯誤訊息

    • 避免一開始就顯示所有錯誤,這會讓使用者感到困惑

  2. 漸進式驗證

    • 先顯示必填錯誤

    • 當欄位有值後,再顯示格式錯誤

  3. 視覺反饋

    • 使用顏色、圖標等視覺元素指示欄位狀態

    • 考慮無障礙性,不要只依賴顏色來傳達信息

  4. 即時驗證

    • 在使用者輸入時或失焦後立即驗證

    • 提供即時反饋,而不是等到表單提交時

Last updated