非同步驗證器(Async Validator)

非同步驗證器同樣接收表單控制項(control)作為參數,但必須返回一個 Observable。這讓我們可以執行非同步操作,例如發送 HTTP 請求到後端驗證資料。

基本結構

typescriptCopyimport { AbstractControl, AsyncValidatorFn, ValidationErrors } from '@angular/forms';
import { Observable, of } from 'rxjs';

function emailIsUnique(control: AbstractControl): Observable<ValidationErrors | null> {
  if (control.value !== 'test@test.com') {
    // 這裡只是模擬,實際使用時會呼叫 API
    return of(null);  // 驗證通過
  }
  return of({ notUnique: true });  // 驗證失敗
}

使用方式:

typescriptCopyemail: new FormControl('', {
  validators: [Validators.required, Validators.email],
  asyncValidators: [emailIsUnique],
}),

重要特性

  1. 觸發時機:非同步驗證器只有在所有同步驗證器都通過後才會被觸發,這樣可以避免不必要的 API 調用。

  2. Pending 狀態:當非同步驗證進行時,表單控制項會進入 "pending" 狀態,可以用來顯示載入指示器:

    htmlCopy@if(loginForm.controls.email.pending){
      <p>Checking email...</p>
    }
  3. 實際 HTTP 請求:在實際應用中,通常會使用服務來發送 HTTP 請求:

    typescriptCopy// 簡化的示例
    function emailIsUnique(userService: UserService): AsyncValidatorFn {
      return (control: AbstractControl): Observable<ValidationErrors | null> => {
        return userService.checkEmailExists(control.value).pipe(
          map(exists => exists ? { notUnique: true } : null)
        );
      };
    }
  4. 防抖處理:為避免每次按鍵都發送請求,通常會添加防抖:

    typescriptCopy// 加入防抖處理的概念性示範
    import { debounceTime, switchMap } from 'rxjs/operators';
    
    return of(control.value).pipe(
      debounceTime(500),  // 500ms 防抖
      switchMap(value => userService.checkEmailExists(value))
    );

非同步驗證器非常適合用於檢查值的唯一性,例如驗證使用者名稱或電子郵件是否已被使用。在表單提交前,所有的非同步驗證器都必須完成並通過,表單才會被視為有效。

Last updated