簡體   English   中英

去抖動異步驗證器

[英]Debounce async Validator

我有一個工作異步驗證器,它向服務器發出 HTTP 請求以檢查用戶名是否已被占用。 因為我不想在每次擊鍵后調用 API,所以我需要對輸入流進行去抖動。

我第一次throttleTime的服務,但對SO另一個話題說,這已經是對誰訂閱了一個,但還沒有運氣!

我的組件:

this.form = this._fb.group(
      {
        username: ['', [Validators.required, Validators.maxLength(50), NoWhitespaceValidator], [IsUserIdFreeValidator.createValidator(this._managementService)]]
      });

我的驗證器:

export class IsUserIdFreeValidator {
  static createValidator(_managementService: ManagementService) {
    return (control: AbstractControl) => {
      return _managementService.isUserIdFree(control.value)
        .pipe(
          throttleTime(5000),
          (map(
            (result: boolean) => result === false ? { isUserIdFree: true } : null))
        );
    };
  }
}

我的服務:

  public isUserIdFree(userId: string): Observable<{} | boolean | HttpError> {
    const updateUserCheck: UpdateUserCheck = new UpdateUserCheck();
    updateUserCheck.userID = userId;

    return this._httpClient.post<boolean>('UserManagementUser/IsUserIdFree', updateUserCheck));
  }

這應該可以解決問題:

  static createValidator(_managementService: ManagementService) {
    const subject = new BehaviorSubject('');
    const observable = subject.asObservable().pipe(
        debounceTime(1000),
        switchMap(val => _managementService.isUserIdFree(val)),
        map((isUserIdFree: boolean) => isUserIdFree ?  null : { userIdTaken : true }),
        ); 
    return (control: AbstractControl) => {
      subject.next(control.value);
      return observable.pipe(takeUntil(timer(5000))); // acts as a way to make observable finite
    }
  }

去抖動應該發生在控件發出的值上,而不是從 http 服務返回的結果上。 我們首先在可觀察流上發出值,並通過distinctUntilChanged將其通過管道傳輸,這確保只有與上次發出的值相比的不同值才能通過管道的那個階段。 debounceTime(x)確保僅在發出 'x' 毫秒后的最后一個值。

switchMap操作符獲取控制值並向后端發出 get 請求,並將新的 observable 傳遞到管道的下一個階段。 最后,我已將您現有的地圖運算符應用於后端的結果以生成適當的錯誤消息。

我能夠通過另一種方式解決這個問題,通過控件上名為“ updateOn ”的屬性

使用模板驅動的表單:

<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">

具有反應形式:

new FormControl('', {updateOn: 'blur'});

如果您使用表單構建器,從 Angular 7 開始,您可以使用

this.form = this._fb.group({
  email: ['', [CustomValidators.email], null, 'blur'],
});

使用 observables,我做了:

form = this._fb.group({
email: [null, [Validators.required, Validators.email], [this.validateEmailNotTaken.bind(this)]]
})

private _emailCancel$ = new Subject<void>();
 validateEmailNotTaken(control: AbstractControl): Observable<object> {
        this._emailCancel$.next();
        return race(
            timer(3000).pipe(map(_ => true)),
            this._emailCancel$.pipe(
                map(_ => false)
            )
        ).pipe(
            take(1),
            filter(val => !!val),
            switchMap(() => this.isEmailTaken(control.value).pipe(
                map(res => res ? {isTaken: true } : null)
            ))
        );
    }

任何人發現這個問題和答案的設置,恕我直言,這個問題是一個重復的這一個 我認為這個答案是正確的,除非你喜歡 Promises。 我打算用更新的(Angular 9+)解決方案在那里發布答案。

編輯: 這是我對如何做到這一點的建議

順便說一句,在遇到這個問題和另一個問題之前,我嘗試了此處列出的所有三個答案。 您可以讓它們全部工作,但我鏈接的答案無疑是最優雅的。 很難弄清楚,因為它依賴於真正應該在文檔中的 Angular 框架中隱藏的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM