亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

angular異步驗(yàn)證器防抖實(shí)例詳解

 更新時(shí)間:2022年03月31日 17:10:21   作者:weiewiyi  
在實(shí)際工作中,我們經(jīng)常需要一個(gè)基于后端API驗(yàn)證值的驗(yàn)證器,下面這篇文章主要給大家介紹了關(guān)于angular異步驗(yàn)證器防抖的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

背景:

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

原版異步驗(yàn)證器:

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));
    };
  }

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

防抖節(jié)流

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

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

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

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

定義:

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

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

舉個(gè)例子來(lái)說(shuō)明:

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

閘機(jī)開(kāi)之后,等待3秒,如果中又有人通過(guò),3秒等待重新計(jì)時(shí),直到3秒后沒(méi)人通過(guò)后關(guān)閉,這是防抖。

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

代碼實(shí)現(xiàn):

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

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

這里僅是說(shuō)明angular中formContorl異步驗(yàn)證器如何防抖的步驟:

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

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

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

疑惑

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

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

原來(lái)的版本是這么使用的:

return this.vehicleBrandService.existByName(...)

改寫后是這么使用的:

return control.valueChanges.pipe(...

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

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

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

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

first()的使用

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

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

return control.valueChanges.pipe(
           first() 
)

單元測(cè)試

一個(gè)好的功能要有一個(gè)好的單元測(cè)試。

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();
     ...
}));

原來(lái)的時(shí)候我寫的單元測(cè)試說(shuō)這樣的,

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

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

所以這里用到了fakeAsync;

fakeAsync;

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

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

官方測(cè)試代碼:

仿照測(cè)試代碼:

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

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

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

并且,這兩條語(yǔ)句幾乎是同時(shí)打印的,也就是說(shuō),單元測(cè)試并沒(méi)有讓我們真的等待10000000ms。

所以經(jīng)過(guò)測(cè)試時(shí)候我們就可以使用tick(1000)和fakeAsync模擬防抖時(shí)間結(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();

  }));

題外

寫后臺(tái)的時(shí)候還遇到了一個(gè)錯(cuò)誤:

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

打了很多斷點(diǎn)都沒(méi)發(fā)現(xiàn)問(wèn)題。

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

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

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

jpa:
    hibernate:
      ddl-auto: update

補(bǔ)充

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

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

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

簡(jiǎn)單來(lái)說(shuō)就是當(dāng) timer 結(jié)束時(shí)發(fā)出一個(gè)值。

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

當(dāng)然只是猜測(cè),有機(jī)會(huì)再補(bǔ)充,經(jīng)過(guò)測(cè)試防抖功能是正常的。

export class VehicleBrandAsyncValidator {
  /**
   * 防抖時(shí)間
   */
  debounceTime = 1000;
  
  constructor(private vehicleBrandService: VehicleBrandService) { }

  /**
   * 驗(yàn)證方法,車輛品牌名稱
   */
  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)),
        // 對(duì)結(jié)果進(jìn)行處理,null表示正確,對(duì)象表示錯(cuò)誤
        map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)),
      )
    };
  }
}

總結(jié)

到此這篇關(guān)于angular異步驗(yàn)證器防抖的文章就介紹到這了,更多相關(guān)angular異步驗(yàn)證器防抖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論