Angular 5 Debounce自定义异步验证器

时间:2018-10-12 15:27:47

标签: angular typescript angular5

我创建了一个自定义异步验证器,该验证器使用服务来验证服务器上的电子邮件。但是,这意味着每次输入字符都会打服务器,这是不好的。在这里,我已经遵循了几个无法继续工作的答案。

我的验证者:

import {FormControl, NG_ASYNC_VALIDATORS, Validator} from 
'@angular/forms';
import { Http } from '@angular/http';
import {Directive, forwardRef} from "@angular/core";
import {ValidateEmailService} from "../services/validate-email.service";
import {UserService} from "../services/user.service";

@Directive({
  selector: '[appEmailValidator]',
  providers: [
    { provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => EmailValidator), multi: true }
  ]
})
export class EmailValidator implements Validator {
  public validateEmailService: ValidateEmailService;

  constructor(
    private _http: Http,
    private _userService: UserService
  ) {
    this.validateEmailService = new ValidateEmailService(this._http, this._userService);
  }

  validate(c: FormControl) {
    return new Promise(resolve => {
      this.validateEmailService.validateEmail(c.value)
        .subscribe((res) => {
          console.log(res);
          if (res.valid) {
            resolve(null);
          } else {
            resolve({
              valid: {
                valid: false
              }
            });
          }
        });
      })
    }
}

它本身可以很好地工作,但是一旦我尝试向其添加某种形式的去抖动功能,我最终就将其破坏了。

我尝试了this question的答案,但在Type X is not assignable to type 'Observable<any>'等方面却出现了错误。

我通过使用setTimeout接近了,但最终所做的只是停止功能。

我的最终目标是仅在输入未更改约600 ms的情况下运行验证器,但会确定每600-2000 ms仅验证一次。

为更加清楚起见,请使用validateEmail中的ValidateEmailService方法:

public validateEmail(email: string) {

  let validateEmail = new ValidateEmail(email);

  return this._http.get(
    this.getUrl(validateEmail),
    this.getOptionArgs())
    .map((response: Response) => Object.assign(new UserEmailVerification(), response.json().UserEmailVerification));

}

2 个答案:

答案 0 :(得分:2)

我还没有看到作为指令实现的异步验证器,而是分配给表单控件的验证器功能。

这是我用于类似情况的示例验证器:

import { Injectable } from '@angular/core';
import { AbstractControl, AsyncValidatorFn } from '@angular/forms';
import { Observable, timer, of } from 'rxjs';
import { switchMap, map } from 'rxjs/operators';

import { MembershipsService } from '@app/memberships/memberships.service';

@Injectable()
export class MembershipsValidators {

  constructor (
    private membershipsService: MembershipsService,
  ) {}

  checkMembershipExists(email?: string): AsyncValidatorFn {
    return (control: AbstractControl): Observable<{ [key: string]: any } | null> => {
      if (control.value === null || control.value.length === 0) {
        return of(null);
      }
      else if (email && email === control.value) {
        return of(null);
      }
      else {
        return timer(500).pipe(
          switchMap(() => {
            return this.membershipsService.lookupMember(control.value).pipe(
              map(member => {
                if (!member) {
                  return { noMembership: { value: control.value } };
                }

                return null;
              })
            );
          })
        );
      }
    };
  }

}

它被导入并应用于表单控件,例如:

this.form = this.formBuilder.group({
  memberEmail: new FormControl('', {
    validators: [ Validators.required, Validators.pattern(regexPatterns.email) ],
    asyncValidators: [ this.membershipsValidators.checkMembershipExists() ],
  }),
});

这样,直到满足同步验证器的要求时,异步验证器才会触发。

答案 1 :(得分:2)

您可以在诺言中创建一个Observable来完成反跳操作。

这种逻辑可能不会被剪切和粘贴,但是应该使您接近。

import {distinctUntilChanged, debounceTime, switchMap} from 'rxjs/operators';

 validate(c: FormControl) {
  return new Promise(resolve => {
    new Observable(observer => observer.next(c.value)).pipe(
      debounceTime(600),
      distinctUntilChanged(),
      switchMap((value) => { return this.validateEmailService.validateEmail(value) })
    ).subscribe(
      (res) => {
        console.log(res);
        if (res.valid) {
          resolve(null);
        } else {
          resolve({
            valid: {
              valid: false
            }
          });
        }
      }
    )
  })
}