使用去抖动进行异步表单验证?

时间:2016-07-11 11:14:07

标签: angular angular2-forms

我想知道如何在异步验证器上实现去抖时间。

我有以下内容:

...
password: ['',Validators.compose([Validators.required, this.passwordValid])]
...

其中:

passwordValid(control:Control):{ [key: string]: any; } {
    return new Promise(resolve => {
        this._http.post('/passwordCheck', control.value)
            .subscribe(
                success=>{
                    resolve(null);
                },
                error=>{
                    resolve({passwordValid: false})
                }
            )
    })
}

但是现在,每次击键都会触发验证。我需要添加去抖功能。我怎么能这样做?

2 个答案:

答案 0 :(得分:4)

由于在input事件用于触发更新时直接触发验证器,因此无法开箱即用。请参阅源代码中的这一行:

如果您想在此级别利用去抖时间,则需要获得与相应DOM元素的input事件直接链接的observable。 Github中的这个问题可以为您提供上下文:

在您的情况下,解决方法是使用fromEvent可观察方法实现自定义值访问器。

以下是一个示例:

const DEBOUNCE_INPUT_VALUE_ACCESSOR = new Provider(
  NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DebounceInputControlValueAccessor), multi: true});

@Directive({
  selector: '[debounceTime]',
  //host: {'(change)': 'doOnChange($event.target)', '(blur)': 'onTouched()'},
  providers: [DEBOUNCE_INPUT_VALUE_ACCESSOR]
})
export class DebounceInputControlValueAccessor implements ControlValueAccessor {
  onChange = (_) => {};
  onTouched = () => {};
  @Input()
  debounceTime:number;

  constructor(private _elementRef: ElementRef, private _renderer:Renderer) {

  }

  ngAfterViewInit() {
    Observable.fromEvent(this._elementRef.nativeElement, 'keyup')
      .debounceTime(this.debounceTime)
      .subscribe((event) => {
        this.onChange(event.target.value);
      });
  }

  writeValue(value: any): void {
    var normalizedValue = isBlank(value) ? '' : value;
    this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
  }

  registerOnChange(fn: () => any): void { this.onChange = fn; }
  registerOnTouched(fn: () => any): void { this.onTouched = fn; }
}

并以这种方式使用它:

function validator(ctrl) {
  console.log('validator called');
  console.log(ctrl);
}

@Component({
  selector: 'app'
  template: `
    <form>
      <div>
        <input [debounceTime]="2000" [ngFormControl]="ctrl"/>
      </div>
      value : {{ctrl.value}}
    </form>
  `,
  directives: [ DebounceInputControlValueAccessor ]
})
export class App {
  constructor(private fb:FormBuilder) {
    this.ctrl = new Control('', validator);
  }
}

请参阅此plunkr:https://plnkr.co/edit/u23ZgaXjAvzFpeScZbpJ?p=preview

答案 1 :(得分:1)

另一个更简单的实现可以如下面的答案所述:

https://stackoverflow.com/a/38022310/4655056

您还可以在创建新订阅/请求之前更新该实现以取消订阅活动订阅。

这是我的异步验证器示例:

private _uniqueUsernameValidator(control: FormControl): Promise<any> {
if (this._uniqueUsernameSubscription) {
  this._uniqueUsernameSubscription.unsubscribe();
}

if (this._uniqueUsernameTimeout) {
  clearTimeout(this._uniqueUsernameTimeout);
}

return new Promise((resolve, reject) => {
  this._uniqueUsernameTimeout = setTimeout(() => {
    this._uniqueUsernameSubscription = this._SecurityResource.getUsernameExists(control.value).subscribe(resp => {
      if (resp === false) {
        resolve(null);
      } else {
        resolve({customMessage: {message: 'This username is already taken'}});
      }
    });
  }, 1000);
});

}

相关问题