angular異步驗證防抖爬坑記

本週的一個需求是在一個異步驗證上加上防抖,防抖,之前也接觸過,依稀記得不難,就沒再去看angluar的官方文檔,直接就開始google寫了,而後……一不當心就掉坑裏了。後端

忽然沒了效果的驗證功能

在網上一番查詢之後,選了一個最簡單,最符合的實現
image.png異步

一番修改之後個人代碼成了下面這樣ide

public oldPasswordValidator(): AsyncValidatorFn {
    return (ctrl: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
   rerutn ctrl.valueChanges.pipe(
        debounceTime(2000),
        distinctUntilChanged(),
        switchMap(value => this.checkPasswordIsRight(value)),
        map((isRight: boolean) => (isRight ? null : {passwordError: true})),
        catchError(() => null));
    };

可是驗證器並無效果,在c層獲取到的始終是null,而在方法中打印結果,卻能夠發現,可以產生正確的結果
image.png
可是獲取到的相關錯誤一直是沒有,更讓我感到驚訝的是表單的validinvalid居然同時處在了false,他們不是應該互斥嗎?不是反義詞嗎?
image.png性能

FormControl的status

首先仍是解決validinvalid的疑惑吧,查詢官方文檔。ui

FormControl的status繼承自AbstractControlthis

AbstractControl是 FormControl、FormGroup 和 FormArray 的基類。它提供了一些全部控件和控件組共有的行爲,好比運行驗證器、計算狀態和重置狀態。 它還定義了一些全部子類共享的屬性,如 value、valid 和 dirty。不容許直接實例化它。

找到valid
image.pnggoogle

至此,這個疑問解決了,互斥的並不只僅是validinvalid,lua

還有一個檢查進行狀態:pending和禁止disable,驗證一下,果真此時的狀態是在pendingspa

image.png

爲什麼會一直在pending狀態?

接着查看表單驗證的官方文檔3d

自定義異步驗證器和同步驗證器很像,只是它們必須返回一個稍後會輸出 null 或「驗證錯誤對象」的承諾(Promise)或可觀察對象,若是是可觀察對象,那麼它必須在某個時間點被完成(complete),那時候這個表單就會使用它輸出的最後一個值做爲驗證結果。(譯註:HTTP 服務是自動完成的,可是 某些自定義的可觀察對象可能須要手動調用 complete 方法)

返回的可觀察對象必須是有限的,也就是說,它必須在某個時間點結束(complete)。要把無盡的可觀察對象轉換成有限的,可使用 first、last、take 或 takeUntil 等過濾型管道對其進行處理

結果很明顯了,上面的觀察者對象是經過valueChangs產生的, 被人訂閱後,並不會主動的調用complete()方法,而會不停的發佈新值。下面的例子能夠看出來
image.png

給他加個first()讓他只返回第一個結果,果真好使了:

image.png

first(predicate: function(value: T, index: number, source: Observable<T>): boolean, resultSelector:function(value: T, index: number): R, defaultValue: R): Observable<T | R>

只發出由源 Observable 所發出的值中第一個(或第一個知足條件的值)。

若是你足夠細心

image.png

看的太不仔細了,到解決這個問題以前,一直都沒發現……

一個更簡單的實現方式

驗證的防抖功能實際上並不須要經過上面的方式實現,儘管上面也並不困難。

最簡單的防抖方式,在官方文檔已經提供了:

默認狀況下,每當表單值變化以後,都會執行全部驗證器。對於同步驗證器,沒有什麼會顯著影響應用性能的地方。不過,異步驗證器一般會執行某種 HTTP 請求來對控件進行驗證。若是在每次按鍵以後都發出 HTTP 請求會給後端 API 帶來沉重的負擔,應該儘可能避免。

咱們能夠把updateOn屬性從change(默認值)改爲submitblur來推遲表單驗證的更新時機。

/**
   * 初始化表單
   */
  initForm() {
    this.modifyPasswordForm = this.fb.group({
        oldPassword: [null, [Validators.required], [this.userService.oldPasswordValidator()]],
        newPassword: [null, Validators.required],
        confirmNewPassword: [null, Validators.required]

        // updateOn 做用是在何時更新表單數據
        // https://angular.cn/guide/form-validation#note-on-performance
      }, {updateOn: 'blur'},
    );
  }

想了解更多updateOn的內容,可查看這篇文章

總結

此次的問題能夠徹底說出在了看文檔不仔細,神奇的是看了幾遍那個StackOverflow上的回答都沒發現他比我多了一個first(),雖然所以浪費了很多的時間,但收穫也是巨大的,這波不虧。

相關文章
相關標籤/搜索