溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

angular異步驗證器防抖的方法

發(fā)布時間:2022-04-01 10:29:05 來源:億速云 閱讀:287 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“angular異步驗證器防抖的方法”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

    背景:

    當(dāng)前輸入框的formControl設(shè)置了異步驗證器,會根據(jù)當(dāng)前的值進(jìn)行請求后臺,判斷數(shù)據(jù)庫中是否存在。

    angular異步驗證器防抖的方法

    原版異步驗證器:

    vehicleBrandNameNotExist(): AsyncValidatorFn {
        return (control: AbstractControl): Observable<ValidationErrors | null> => {
          if (control.value === '') {
            return of(null);
          }
          return this.vehicleBrandService.existByName(control.value).pipe(map(exists => exists ? {vehicleBrandNameExist: true} : null));
        };
      }

    但是測試下來發(fā)現(xiàn),該異步驗證器觸發(fā)的太頻繁了。輸入框每輸入一個字母都會對后臺進(jìn)行請求,不利于節(jié)省資源。

    防抖節(jié)流

    這個相關(guān)的操作叫做防抖和節(jié)流。什么是防抖和節(jié)流?有什么區(qū)別?

    本質(zhì)上是一種優(yōu)化高頻率執(zhí)行代碼的一種手段。

    比如瀏覽器的鼠標(biāo)點擊,鍵盤輸入等事件觸發(fā)時,會高頻率地調(diào)用綁定在事件上的回調(diào)函數(shù),一定程度上影響著資源的利用。

    為了優(yōu)化,我們需要 防抖(debounce) 和 節(jié)流(throttle) 的方式來減少調(diào)用頻率。

    定義:

    防抖: n 秒后在執(zhí)行該事件,若在 n 秒內(nèi)被重復(fù)觸發(fā),則重新計時

    節(jié)流: n 秒內(nèi)只運行一次,若在 n 秒內(nèi)重復(fù)觸發(fā),只有一次生效

    舉個例子來說明:

    乘坐地鐵,過閘機時,每個人進(jìn)入后3秒后門關(guān)閉,等待下一個人進(jìn)入。

    閘機開之后,等待3秒,如果中又有人通過,3秒等待重新計時,直到3秒后沒人通過后關(guān)閉,這是防抖

    閘機開之后,每3秒后準(zhǔn)時關(guān)閉一次,間隔時間執(zhí)行,這是節(jié)流

    代碼實現(xiàn):

    防抖操作恰好符合我們的需求。

    找異步驗證器中防抖的代碼實現(xiàn)中恰好看到了liyiheng學(xué)長的文章:
    http://www.kemok4.com/article/175497.htm,于是便參考了一下。

    這里僅是說明angular中formContorl異步驗證器如何防抖的步驟:

    1.創(chuàng)建(改寫)異步驗證器

    vehicleBrandNameNotExist(): AsyncValidatorFn {
        return (control: AbstractControl): Observable<ValidationErrors | null> => {
          if (control.value === '') {
            return of(null);
          }
          return control.valueChanges.pipe(
            // 防抖時間,單位毫秒
            debounceTime(1000),
            // 過濾掉重復(fù)的元素
            distinctUntilChanged(),
            // 調(diào)用服務(wù), 獲取結(jié)果
            switchMap(value => this.vehicleBrandService.existByName(value)),
            // 對結(jié)果進(jìn)行處理,null表示正確,對象表示錯誤
            map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)),
            // 每次驗證的結(jié)果是唯一的,截斷流
            first()
          )
        };
      }
    • 添加異步驗證器

    let formControl = new FormControl('', [], asyncValidate.vehicleBrandNameNotExist());

    之后我們在v層在相關(guān)的標(biāo)簽上綁定該fromControl就可以了。

    疑惑

    相關(guān)操作到這里就結(jié)束了,能夠正常使用了。

    但是改寫之后還有些疑惑。

    原來的版本是這么使用的:

    return this.vehicleBrandService.existByName(...)

    改寫后是這么使用的:

    return control.valueChanges.pipe(...

    改寫后使用了valueChanges,也就是產(chǎn)生了一個observable,它使得每當(dāng)控件的值在更改時,它都會發(fā)出一個事件。

    那么,每次調(diào)用異步驗證器之后,我們每次都用valueChanges,每次的observable是不是同一個?

    于是我進(jìn)行了測試:
    原理:多次調(diào)用異步驗證器,并緩存ovservable,如果不相同則輸出 “不相等”

    angular異步驗證器防抖的方法

    測試結(jié)果:如圖,只是在第一次初始化的時候輸出了不相等,因為第一次observable為undefined, 在有值之后,多次調(diào)用異步驗證器發(fā)現(xiàn)observabel始終是同一個

    angular異步驗證器防抖的方法

    first()的使用

    之前也不理解first的使用,看學(xué)長的文章之后才明白,first()來避免多次地這樣返回值。

    angular異步驗證器防抖的方法

    所以我們產(chǎn)生的observable一直處于pending狀態(tài),需要用first讓它返回第一個值就好。

    return control.valueChanges.pipe(
               first() 
    )

    單元測試

    一個好的功能要有一個好的單元測試。

    1 it('should create an instance', async () => {
     2   expect(asyncValidate).toBeTruthy();
     3   let formControl = new FormControl('', [], asyncValidate.vehicleBrandNameNotExist());
     4   formControl.setValue('重復(fù)車輛品牌');
     5    // 等待防抖結(jié)束
     6   await new Promise(resolve => setTimeout(resolve, 1000));
    
     7   getTestScheduler().flush();
     8   expect(formControl.errors.vehicleBrandNameExist).toBeTrue();
         ...
    }));

    原來的時候我寫的單元測試說這樣的,

    等待防抖結(jié)束我用了await new Promise 以及setTimeout。執(zhí)行到第8行的時候,讓線程等待1秒。

    經(jīng)過老師指正之后,發(fā)現(xiàn)這樣并不好。假如某個測試需要等待一個小時,那么我們的執(zhí)行時間就需要1個小時,這顯然是不現(xiàn)實的。

    所以這里用到了fakeAsync;

    fakeAsync;

    fakeAsync,字面上就是假異步,實際上還是同步進(jìn)行的。

    使用tick()模擬時間的異步流逝。

    官方測試代碼:

    angular異步驗證器防抖的方法

    仿照測試代碼:

    我在tick()前后,打印了new Date(),也就是當(dāng)時的時間,結(jié)果是什么呢?

    angular異步驗證器防抖的方法

    可以看到第一個打印了17:19:30,也就是當(dāng)時測試的時間。

    但是在tick(10000000)后,打印的時間是20:06:10, 達(dá)到了一個未來的時間。

    并且,這兩條語句幾乎是同時打印的,也就是說,單元測試并沒有讓我們真的等待10000000ms。

    angular異步驗證器防抖的方法

    所以經(jīng)過測試時候我們就可以使用tick(1000)和fakeAsync模擬防抖時間結(jié)束了。

    it('should create an instance', fakeAsync( () => {
        expect(asyncValidate).toBeTruthy();
        let formControl = new FormControl('', [], asyncValidate.vehicleBrandNameNotExist());
        formControl.setValue('重復(fù)車輛品牌');
        // 等待防抖結(jié)束
        tick(1000);
        getTestScheduler().flush();
        expect(formControl.errors.vehicleBrandNameExist).toBeTrue();
    
      }));

    題外

    寫后臺的時候還遇到了一個錯誤:

    angular異步驗證器防抖的方法

    它說我color沒有設(shè)置默認(rèn)值,但是回去一看明明已經(jīng)設(shè)置了。

    angular異步驗證器防抖的方法

    打了很多斷點都沒發(fā)現(xiàn)問題。

    后來到數(shù)據(jù)庫一看,好家伙,怎么有兩個,一個是colour,一個是color.

    angular異步驗證器防抖的方法

    之后翻看之前提交的代碼,發(fā)現(xiàn)是之前用的是color,后面換成了colour。

    但是我jpa hibernate設(shè)置的是update,所以數(shù)據(jù)庫對應(yīng)執(zhí)行的是更新,所以上次的字段并沒有刪除,這才導(dǎo)致了數(shù)據(jù)庫有兩個字段。之后刪除其中一個了就沒事了。

    jpa:
        hibernate:
          ddl-auto: update

    補充

    后面谷歌之后發(fā)現(xiàn)了一種比較簡潔也好理解的方法:

    不用調(diào)用first()之類的操作符, 把timer()的返回值作為一個observable就可以了。

    time的作用在這里:
    https://rxjs-cn.github.io/lea...

    簡單來說就是當(dāng) timer 結(jié)束時發(fā)出一個值。

    這個原理猜測可能是當(dāng)timer還沒有結(jié)束并重復(fù)調(diào)用異步驗證器時,表單就不管這個timer了,轉(zhuǎn)而關(guān)注新的。

    當(dāng)然只是猜測,有機會再補充,經(jīng)過測試防抖功能是正常的。

    export class VehicleBrandAsyncValidator {
      /**
       * 防抖時間
       */
      debounceTime = 1000;
      
      constructor(private vehicleBrandService: VehicleBrandService) { }
    
      /**
       * 驗證方法,車輛品牌名稱
       */
      vehicleBrandNameNotExist(): AsyncValidatorFn {
        return (control: AbstractControl): Observable<ValidationErrors | null> => {
          if (control.value === '') {
            return of(null);
          }
          return timer(this.debounceTime).pipe(
            // 調(diào)用服務(wù), 獲取結(jié)果
            switchMap(() => this.vehicleBrandService.existByName(control.value)),
            // 對結(jié)果進(jìn)行處理,null表示正確,對象表示錯誤
            map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)),
          )
        };
      }
    }

    “angular異步驗證器防抖的方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI