angular异步验证器防抖的方法(angular,开发技术)

时间:2024-05-03 13:45:32 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

    背景:

    当前输入框的formControl设置了异步验证器,会根据当前的值进行请求后台,判断数据库中是否存在。

    angular异步验证器防抖的方法

    原版异步验证器:

    vehicleBrandNameNotExist():AsyncValidatorFn{return(control:AbstractControl):Observable<ValidationErrors|null>=>{if(control.value===''){returnof(null);}returnthis.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.yisu.com/article/175497.htm,于是便参考了一下。

    这里仅是说明angular中formContorl异步验证器如何防抖的步骤:

    1.创建(改写)异步验证器

    vehicleBrandNameNotExist():AsyncValidatorFn{return(control:AbstractControl):Observable<ValidationErrors|null>=>{if(control.value===''){returnof(null);}returncontrol.valueChanges.pipe(//防抖时间,单位毫秒debounceTime(1000),//过滤掉重复的元素distinctUntilChanged(),//调用服务,获取结果switchMap(value=>this.vehicleBrandService.existByName(value)),//对结果进行处理,null表示正确,对象表示错误map((exists:boolean)=>(exists?{vehicleBrandNameExist:true}:null)),//每次验证的结果是唯一的,截断流first())};}
    • 添加异步验证器

    letformControl=newFormControl('',[],asyncValidate.vehicleBrandNameNotExist());

    之后我们在v层在相关的标签上绑定该fromControl就可以了。

    疑惑

    相关操作到这里就结束了,能够正常使用了。

    但是改写之后还有些疑惑。

    原来的版本是这么使用的:

    returnthis.vehicleBrandService.existByName(...)

    改写后是这么使用的:

    returncontrol.valueChanges.pipe(...

    改写后使用了valueChanges,也就是产生了一个observable,它使得每当控件的值在更改时,它都会发出一个事件。

    那么,每次调用异步验证器之后,我们每次都用valueChanges,每次的observable是不是同一个?

    于是我进行了测试:
    原理:多次调用异步验证器,并缓存ovservable,如果不相同则输出 “不相等”

    angular异步验证器防抖的方法

    测试结果:如图,只是在第一次初始化的时候输出了不相等,因为第一次observable为undefined, 在有值之后,多次调用异步验证器发现observabel始终是同一个

    angular异步验证器防抖的方法

    first()的使用

    之前也不理解first的使用,看学长的文章之后才明白,first()来避免多次地这样返回值。

    angular异步验证器防抖的方法

    所以我们产生的observable一直处于pending状态,需要用first让它返回第一个值就好。

    returncontrol.valueChanges.pipe(first())

    单元测试

    一个好的功能要有一个好的单元测试。

    1it('shouldcreateaninstance',async()=>{2expect(asyncValidate).toBeTruthy();3letformControl=newFormControl('',[],asyncValidate.vehicleBrandNameNotExist());4formControl.setValue('重复车辆品牌');5//等待防抖结束6awaitnewPromise(resolve=>setTimeout(resolve,1000));7getTestScheduler().flush();8expect(formControl.errors.vehicleBrandNameExist).toBeTrue();...}));

    原来的时候我写的单元测试说这样的,

    等待防抖结束我用了await new Promise 以及setTimeout。执行到第8行的时候,让线程等待1秒。

    经过老师指正之后,发现这样并不好。假如某个测试需要等待一个小时,那么我们的执行时间就需要1个小时,这显然是不现实的。

    所以这里用到了fakeAsync;

    fakeAsync;

    fakeAsync,字面上就是假异步,实际上还是同步进行的。

    使用tick()模拟时间的异步流逝。

    官方测试代码:

    angular异步验证器防抖的方法

    仿照测试代码:

    我在tick()前后,打印了new Date(),也就是当时的时间,结果是什么呢?

    angular异步验证器防抖的方法

    可以看到第一个打印了17:19:30,也就是当时测试的时间。

    但是在tick(10000000)后,打印的时间是20:06:10, 达到了一个未来的时间。

    并且,这两条语句几乎是同时打印的,也就是说,单元测试并没有让我们真的等待10000000ms。

    angular异步验证器防抖的方法

    所以经过测试时候我们就可以使用tick(1000)和fakeAsync模拟防抖时间结束了。

    it('shouldcreateaninstance',fakeAsync(()=>{expect(asyncValidate).toBeTruthy();letformControl=newFormControl('',[],asyncValidate.vehicleBrandNameNotExist());formControl.setValue('重复车辆品牌');//等待防抖结束tick(1000);getTestScheduler().flush();expect(formControl.errors.vehicleBrandNameExist).toBeTrue();}));

    题外

    写后台的时候还遇到了一个错误:

    angular异步验证器防抖的方法

    它说我color没有设置默认值,但是回去一看明明已经设置了。

    angular异步验证器防抖的方法

    打了很多断点都没发现问题。

    后来到数据库一看,好家伙,怎么有两个,一个是colour,一个是color.

    angular异步验证器防抖的方法

    之后翻看之前提交的代码,发现是之前用的是color,后面换成了colour。

    但是我jpa hibernate设置的是update,所以数据库对应执行的是更新,所以上次的字段并没有删除,这才导致了数据库有两个字段。之后删除其中一个了就没事了。

    jpa:hibernate:ddl-auto:update

    补充

    后面谷歌之后发现了一种比较简洁也好理解的方法:

    不用调用first()之类的操作符, 把timer()的返回值作为一个observable就可以了。

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

    简单来说就是当 timer 结束时发出一个值。

    这个原理猜测可能是当timer还没有结束并重复调用异步验证器时,表单就不管这个timer了,转而关注新的。

    当然只是猜测,有机会再补充,经过测试防抖功能是正常的。

    exportclassVehicleBrandAsyncValidator{/***防抖时间*/debounceTime=1000;constructor(privatevehicleBrandService:VehicleBrandService){}/***验证方法,车辆品牌名称*/vehicleBrandNameNotExist():AsyncValidatorFn{return(control:AbstractControl):Observable<ValidationErrors|null>=>{if(control.value===''){returnof(null);}returntimer(this.debounceTime).pipe(//调用服务,获取结果switchMap(()=>this.vehicleBrandService.existByName(control.value)),//对结果进行处理,null表示正确,对象表示错误map((exists:boolean)=>(exists?{vehicleBrandNameExist:true}:null)),)};}}
     </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
    本文:angular异步验证器防抖的方法的详细内容,希望对您有所帮助,信息来源于网络。
    上一篇:Nginx虚拟主机怎么配置下一篇:

    7 人围观 / 0 条评论 ↓快速评论↓

    (必须)

    (必须,保密)

    阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18