【问题标题】:Async form validation with debounce?带去抖动的异步表单验证?
【发布时间】:2023-03-12 09:26:02
【问题描述】:

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

我有以下几点:

...
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})
                }
            )
    })
}

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

【问题讨论】:

    标签: angular angular2-forms


    【解决方案1】:

    这是不可能开箱即用的,因为当input 事件用于触发更新时,验证器会被直接触发。在源代码中看到这一行:

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

    在您的情况下,一种解决方法是利用 observable 的 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

    【讨论】:

      【解决方案2】:

      另一个更简单的实现可能如此答案中所述:

      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);
      });
      

      }

      【讨论】:

        猜你喜欢
        • 2019-03-15
        • 2020-02-22
        • 2019-03-23
        • 1970-01-01
        • 1970-01-01
        • 2018-08-17
        • 2019-06-05
        • 2017-10-26
        • 2016-07-03
        相关资源
        最近更新 更多