# 非同步驗證器(Async Validator)

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

#### 基本結構

```typescript
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 });  // 驗證失敗
}
```

使用方式：

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

<figure><img src="https://887996651-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMfWnm9OyvfbzOzPO6ZJt%2Fuploads%2FYTjKXt1mywRo3nLa7Bho%2Fimage.png?alt=media&#x26;token=94ed2bd9-bc3a-4376-bf6e-755bd604e398" alt=""><figcaption></figcaption></figure>

#### 重要特性

1. **觸發時機**：非同步驗證器只有在所有同步驗證器都通過後才會被觸發，這樣可以避免不必要的 API 調用。
2.  **Pending 狀態**：當非同步驗證進行時，表單控制項會進入 "pending" 狀態，可以用來顯示載入指示器：

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

    ```typescript
    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.  **防抖處理**：為避免每次按鍵都發送請求，通常會添加防抖：

    ```typescript
    typescriptCopy// 加入防抖處理的概念性示範
    import { debounceTime, switchMap } from 'rxjs/operators';

    return of(control.value).pipe(
      debounceTime(500),  // 500ms 防抖
      switchMap(value => userService.checkEmailExists(value))
    );
    ```

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