【问题标题】:Why distinctUntilChanged won't work in asyncValidators in angular为什么 distinctUntilChanged 在 angular 中的 asyncValidators 中不起作用
【发布时间】:2020-04-10 13:03:48
【问题描述】:

当我出于某种原因在表单输入上按下一个键时,异步验证器不会检测到 distinctUntilChanged,它仍然会发送 API 请求。 例如,如果我按 35,删除 5,然后再次添加 5,它仍然会发送请求。 这是代码: (我已经尝试了几乎所有方法,但仍然无法正常工作)

validateSomeNumber(control: FormControl): Observable<any> | Promise <any> {
    this.isSubmitBtnDisabled = true;
    return control.valueChanges.pipe(
      debounceTime(1000),
      distinctUntilChanged(),
      switchMap((value) => {
        return this.apiService.someApiRequest({ 'to_number': control.value }).pipe(
          map(res => {
            console.log(res);
            if (res.success) {
              // console.log(res);
              this.isSubmitBtnDisabled = false;
              return null;
            } else {
              // console.log(res);
              this.isSubmitBtnDisabled = true;
              return{ 'invalidCharacters': true };
            }
          }),
        );
      }),
      first()
    );
  }

【问题讨论】:

  • 您确定不是在每次按键更改时都调用此方法吗?因为这样每次都会创建一个新链。
  • 你每次都返回新的 Observable,distinctUntilChanged() 不起作用。

标签: validation asynchronous rxjs operators angular-reactive-forms


【解决方案1】:

默认情况下,validateSomeNumber 在每次值更改后调用。

如果您在每次值更改时返回它

return control.valueChanges.pipe(
  debounceTime(1000),
  distinctUntilChanged(),
  ...
)

您正在为每次值更改创建一个新的值更改 Observable。例如,如果你输入四个字符,你最终会得到四个独立的 Observable,每个发出一个字符,而不是一个 Observable 发出四次。所以debounceTimedistinctUntilChanged 只会影响您在特定值更改时创建的 Observable,但不会影响整个值更改过程。如果它们只影响一个 Observable,一旦它们显然不能按照您的预期工作。

你应该直接返回http请求

validateSomeNumber(control: FormControl): Observable<any> | Promise <any> {
  this.isSubmitBtnDisabled = true;
  return this.apiService.someApiRequest({ 'to_number': control.value }).pipe(
      map(..),
  );
}

限制请求频率

选项 1:更新开启

防止http请求在每次值更改时执行Angular recommends changing the updateOn property to submit or blur

使用模板驱动的表单:

<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">

使用反应形式:

new FormControl('', {updateOn: 'blur'});

{updateOn: 'blur'} 只会在您的输入失去焦点时执行验证器。

选项 2:模拟 debounceTime 和 distinctUntilChanged

如果表单值发生变化,Angular 会自动取消订阅 AsyncValidator 返回的前一个 Observable。这允许您使用timer 模拟debounceTime。要模拟 distinctUntilChanged,您可以跟踪最后一个请求项并自己进行相等性检查。

private lastRequestTerm = null;

validateSomeNumber(control: FormControl): Observable<any> | Promise <any> {
  this.isSubmitBtnDisabled = true;
  // emulate debounceTime
  return timer(1000).pipe(
    // emulate distinceUntilChanged
    filter(_ => control.value != this.lastRequestTerm),
    switchMap(() => {
      this.lastSearchTerm = control.value;
      return this.apiService.someApiRequest({ 'to_number': control.value });
    }),
    map(..)
  );
}

【讨论】:

  • 嘿!非常感谢,效果很好。但我仍然不明白为什么我不能使用 debounceTime 和 distinctUntilChange 如果它们正是出于这些原因而制作的,为什么我需要模仿它?
  • 您之前的方法会在每次值更改时创建一个新的值更改 Observable。所以你最终会得到多个独立的 Observable。如果您输入a,b,c,d,您不会得到一个发出a,b,c,d 的Observable,而是得到4 个发出a 的Observable,另一个发出b,另一个c,另一个发出d。因此,应该处理多个传入值的运算符显然不会按预期工作,因为每个 Observable 只发出一次。
  • 非常感谢!你帮助我了解了发生了什么!
猜你喜欢
  • 2019-12-12
  • 1970-01-01
  • 2018-12-25
  • 1970-01-01
  • 2018-01-15
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 2017-02-22
相关资源
最近更新 更多