<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當前輸入框的formControl設定了非同步驗證器,會根據當前的值進行請求後臺,判斷資料庫中是否存在。
原版非同步驗證器:
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)); }; }
但是測試下來發現,該非同步驗證器觸發的太頻繁了。輸入框每輸入一個字母都會對後臺進行請求,不利於節省資源。
這個相關的操作叫做防抖和節流。什麼是防抖和節流?有什麼區別?
本質上是一種優化高頻率執行程式碼的一種手段。
比如瀏覽器的滑鼠點選,鍵盤輸入等事件觸發時,會高頻率地呼叫繫結在事件上的回撥函數,一定程度上影響著資源的利用。
為了優化,我們需要 防抖(debounce) 和 節流(throttle) 的方式來減少呼叫頻率。
防抖: n 秒後在執行該事件,若在 n 秒內被重複觸發,則重新計時
節流: n 秒內只執行一次,若在 n 秒內重複觸發,只有一次生效
舉個例子來說明:
乘坐地鐵,過閘機時,每個人進入後3秒後門關閉,等待下一個人進入。
閘機開之後,等待3秒,如果中又有人通過,3秒等待重新計時,直到3秒後沒人通過後關閉,這是防抖。
閘機開之後,每3秒後準時關閉一次,間隔時間執行,這是節流
防抖操作恰好符合我們的需求。
找非同步驗證器中防抖的程式碼實現中恰好看到了liyiheng學長的文章:
https://www.jb51.net/article/175497.htm,於是便參考了一下。
這裡僅是說明angular中formContorl非同步驗證器如何防抖的步驟:
1.建立(改寫)非同步驗證器
vehicleBrandNameNotExist(): AsyncValidatorFn { return (control: AbstractControl): Observable<ValidationErrors | null> => { if (control.value === '') { return of(null); } return control.valueChanges.pipe( // 防抖時間,單位毫秒 debounceTime(1000), // 過濾掉重複的元素 distinctUntilChanged(), // 呼叫服務, 獲取結果 switchMap(value => this.vehicleBrandService.existByName(value)), // 對結果進行處理,null表示正確,物件表示錯誤 map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)), // 每次驗證的結果是唯一的,截斷流 first() ) }; }
let formControl = new FormControl('', [], asyncValidate.vehicleBrandNameNotExist());
之後我們在v層在相關的標籤上繫結該fromControl就可以了。
相關操作到這裡就結束了,能夠正常使用了。
但是改寫之後還有些疑惑。
原來的版本是這麼使用的:
return this.vehicleBrandService.existByName(...)
改寫後是這麼使用的:
return control.valueChanges.pipe(...
改寫後使用了valueChanges,也就是產生了一個observable,它使得每當控制元件的值在更改時,它都會發出一個事件。
那麼,每次呼叫非同步驗證器之後,我們每次都用valueChanges,每次的observable是不是同一個?
於是我進行了測試:
原理:多次呼叫非同步驗證器,並快取ovservable,如果不相同則輸出 “不相等”
測試結果:如圖,只是在第一次初始化的時候輸出了不相等,因為第一次observable為undefined, 在有值之後,多次呼叫非同步驗證器發現observabel始終是同一個
之前也不理解first的使用,看學長的文章之後才明白,first()來避免多次地這樣返回值。
所以我們產生的observable一直處於pending狀態,需要用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('重複車輛品牌'); 5 // 等待防抖結束 6 await new Promise(resolve => setTimeout(resolve, 1000)); 7 getTestScheduler().flush(); 8 expect(formControl.errors.vehicleBrandNameExist).toBeTrue(); ... }));
原來的時候我寫的單元測試說這樣的,
等待防抖結束我用了await new Promise 以及setTimeout。執行到第8行的時候,讓執行緒等待1秒。
經過老師指正之後,發現這樣並不好。假如某個測試需要等待一個小時,那麼我們的執行時間就需要1個小時,這顯然是不現實的。
所以這裡用到了fakeAsync;
fakeAsync,字面上就是假非同步,實際上還是同步進行的。
使用tick()模擬時間的非同步流逝。
官方測試程式碼:
仿照測試程式碼:
我在tick()前後,列印了new Date(),也就是當時的時間,結果是什麼呢?
可以看到第一個列印了17:19:30,也就是當時測試的時間。
但是在tick(10000000)後,列印的時間是20:06:10, 達到了一個未來的時間。
並且,這兩條語句幾乎是同時列印的,也就是說,單元測試並沒有讓我們真的等待10000000ms。
所以經過測試時候我們就可以使用tick(1000)和fakeAsync模擬防抖時間結束了。
it('should create an instance', fakeAsync( () => { expect(asyncValidate).toBeTruthy(); let formControl = new FormControl('', [], asyncValidate.vehicleBrandNameNotExist()); formControl.setValue('重複車輛品牌'); // 等待防抖結束 tick(1000); getTestScheduler().flush(); expect(formControl.errors.vehicleBrandNameExist).toBeTrue(); }));
寫後臺的時候還遇到了一個錯誤:
它說我color沒有設定預設值,但是回去一看明明已經設定了。
打了很多斷點都沒發現問題。
後來到資料庫一看,好傢伙,怎麼有兩個,一個是colour,一個是color.
之後翻看之前提交的程式碼,發現是之前用的是color,後面換成了colour。
但是我jpa hibernate設定的是update,所以資料庫對應執行的是更新,所以上次的欄位並沒有刪除,這才導致了資料庫有兩個欄位。之後刪除其中一個了就沒事了。
jpa: hibernate: ddl-auto: update
後面谷歌之後發現了一種比較簡潔也好理解的方法:
不用呼叫first()之類的操作符, 把timer()的返回值作為一個observable就可以了。
time的作用在這裡:
https://rxjs-cn.github.io/lea...
簡單來說就是當 timer 結束時發出一個值。
這個原理猜測可能是當timer還沒有結束並重復呼叫非同步驗證器時,表單就不管這個timer了,轉而關注新的。
當然只是猜測,有機會再補充,經過測試防抖功能是正常的。
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( // 呼叫服務, 獲取結果 switchMap(() => this.vehicleBrandService.existByName(control.value)), // 對結果進行處理,null表示正確,物件表示錯誤 map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)), ) }; } }
到此這篇關於angular非同步驗證器防抖的文章就介紹到這了,更多相關angular非同步驗證器防抖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45