【问题标题】:Form Control Async validation issue表单控制异步验证问题
【发布时间】:2019-12-06 21:34:36
【问题描述】:

我正在尝试在 Angular2 上实现异步验证,但是当我出于某种原因尝试输入某些内容时,去抖动不起作用。它根据我的输入触发多次。同样在加载时我的异步验证器将被触发,从而将状态设置为待处理,因此信息框显示checking...

我是这样实现的:

HTML:

 <div class="form-group d-flex col px-0">
   <label for="coupon" class="col-2">coupon_code:</label
    <div class="col-5 pl-4 pr-0">
     <input
      class="form-control"
      id="coupon"
      type="text"
      name="coupon"
      formControlName="coupon"
      autocomplete="off"
      />
 </div>

TS 文件

表格:

initForm() {
    const email = '';
    const password = '';
    const password_confirmation = '';
    const doctor = '';
    // const license = '';
    const type = 'doctor';
    const company_id = '';
    const corporate_token = '';
    const doctorLastName = '';

    this.signUpForm = this.fb.group(
      {
        coupon: ['',[], this.couponAsyncValidation.bind(this)]
      },
      {
        validator: this.signupFormValidator(),
      }
    );
  }

异步验证码:

 couponAsyncValidation(control: FormControl): Promise<any | null> | Observable<any | null> {
     return new Promise( (res, rej) => {
      control.valueChanges.pipe(
        debounceTime(2000),
        distinctUntilChanged(),
        switchMap(value => this.userService.couponChecker(value)),
        map( (q) => q ),
        first()
      ).subscribe(
        (d) => { 
        console.log(d)
         d.is_coupon_valid ? res(null) : res({invalid: true})
        }
      )
    })
  }

couponAsyncValidation 将在加载时触发,即使我没有触摸输入,状态也只是挂起。

更新

我设法工作的状态。我检查了状态和未决以及是否脏。

剩下的问题是 debounceTimer 不工作

更新 我想我错过了一个可观察的功能。你觉得呢?

更新 这是 2 秒后发送的请求的图像

知道为什么 debounce 不起作用吗?

更新

现在在 @jarek 的帮助下工作。

这里是异步验证的完整代码


import { AbstractControl, AsyncValidatorFn } from '@angular/forms';
import { switchMap, map} from 'rxjs/operators';
import { UserService } from '../user/services/user.service';
import { Observable, timer, of } from 'rxjs';

export class AsyncValidator {
  static couponValidator(miliSec: number, service: UserService): AsyncValidatorFn {
    return (control: AbstractControl): Observable<any | null> => {
      return timer(miliSec).pipe(
        switchMap(() => {
          if ( control.value ) {
            return service.couponChecker(control.value);
          }
          // this one is needed because for some reason on loading, the signup page
          // will immediately trigger this async call which sends a request to the backend
          // even though user has not inputted anything.
          // then sets the status of the form to pending thus resulting to an invalid form
          return of({is_coupon_valid: true});
        }),
        map( (res) =>  res.is_coupon_valid ? null : res )
      );
    };
  }
}

【问题讨论】:

  • 每次值改变时都会调用异步验证器。因此,每次值更改时,您都会创建一个新的 observable 并订阅它。每次值变化时都会发出这个 observble。
  • @JBNizet 我已经知道了。抱歉,如果我的问题不清楚。但是为什么即使我设置了debounceTime 它也会阻止它在设定的时间过去之前进行投射
  • 我错过了对 first() 的调用。

标签: angular observable angular2-observables


【解决方案1】:

我有一些类似的情况,除了我的异步验证器直接在表单上......代码在下面,但它不会完全匹配你需要的东西。我怀疑你的核心问题是你的订阅对您的控件值进行更改,因为将其设置为验证器应该已经导致在值更改时调用该验证器。

validateExists(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
{
    return timer(500).pipe(
            switchMap(() =>
                this.service.checkThingExists(control.value.value1, control.value.value2).pipe(
                    tap(() => setTimeout(() => this.isValid.emit(this.form.get('parcel').valid))),
                    map(valid => (valid ? null : { notFound: true }))
                )
            )
        );
}

【讨论】:

  • 如果我不通过管道传输值更改,那么您将如何在用户键入时从用户那里获取值或输入?
  • 验证函数的输入参数具有值的控制。因此,您只需执行 control.value。每次输入更改时都会调用您的函数,因为您将其设置为验证器。所以你不需要添加另一个层来捕捉值的变化。
  • 我明白了。这是第一个解决方案。但由于这是异步验证,所以每次有键盘敲击或输入时,我都会调用一个 http 请求。所以我尝试使用valueChanges,因为它是一个可观察的,所以我可以通过管道传输它并从 rxjs 添加一个 debounceTime 运算符,这样每次键盘敲击时它都会阻止请求。
  • 在我上面发布的示例中,我使用 timer(500) 来实现类似的事情,但我仍然不需要订阅 valueChanges。 Timer 也是一个 RxJs 函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-25
  • 2016-07-26
  • 2019-04-05
  • 2021-05-11
  • 1970-01-01
  • 2017-03-02
相关资源
最近更新 更多