【问题标题】:General: asynchonious validation in angular2一般:angular2中的异步验证
【发布时间】:2016-02-21 08:12:48
【问题描述】:

自从几个晚上以来,我一直在 augular2 中使用表单验证。

所有基本案例都很容易实现并且工作正常,但我坚持使用异步验证。我创建了一个非常小的示例http://plnkr.co/edit/Xo8xwJjhlHkXrunzS8ZE,但它不起作用。

根据从 model_spec.ts 通过创建控制组注册的测试“应在状态更新为待处理后触发事件”假设以某种方式工作

builder.group({login: ["",Validators.required,validationFuctionWhichReturnsPromise]

我花了整整一个晚上才发现这段代码已经在 alfa-46 中发布(我使用了 alfa-45),并且在更新依赖之后异步验证开始工作。该功能非常新鲜,没有完整的文档记录,但是

(对于那些还没有尝试过的人)基本上异步验证器是一个函数,它有一个控制参数并返回一个验证结果的承诺。有两种注册验证器的方法。 1)我在示例中使用的那个和 2)作为通过 NG_ASYNC_VALIDATORS 提供验证器的指令(请参阅 UniqLoginValidator 和 NgFormControl 以了解其工作原理)。您可以编写多个验证器(尚未测试,但执行此操作的函数在代码中,请参阅https://github.com/angular/angular/commit/cf449dd)。

但是,当我最终启动并运行验证器时,一个新问题出现了。异步验证器非常适合在服务器端验证中使用。但是在每次 keyup 后每次更改 model.fe 后都会调用验证。因此,如果我们将在每个键启动后向服务器发送请求,这将不是太有效的方式;)我检查了它是如何在 angular 1 中完成的,它们有可能消除验证事件。

我的问题是:

  1. 如何使用异步验证器实现节流或去抖动?我看到了一些想法,但没有一个很好(主要是因为他们需要更改角度代码本身)。有没有任何有效的方法可以在不等待新的角度发布的情况下做到这一点?

我正在考虑使用 debounce(来自 underscorejs)扭曲验证器函数,但它不会起作用,因为 angular 每次都希望获得有效的承诺。

我的第二个想法是,如果所有事件都在后台使用 RxJ,那么也许我可以对负责验证的事件流应用去抖动。在 model.ts 中,从验证器返回的承诺更改为可观察的,并添加了新的订阅。我们无权访问 obs(Observable) 以在那里应用去抖动。

  1. 是否有任何方法或 id 可以更改,轻松扩展对表单验证的控制?

我在How to trigger Form Validators in angular2 中发现了一个密切相关的问题

PS 还有其他与异步验证器相关的问题,它仍然处于打开状态https://github.com/angular/angular/issues/1068

【问题讨论】:

标签: validation asynchronous angular


【解决方案1】:

这是一个帮助类,您可以使用它来消除所有异步验证器的抖动:

import {Component} from 'angular2/core';
import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import {Control} from 'angular2/common';

export class AsyncValidator {
_validate;

constructor(validator: (control: Control) => any, debounceTime = 1000) {
    let source: any = new Observable((observer: Observer<Control>) => {
        this._validate = (control) => observer.next(control);
    });

    source.debounceTime(debounceTime)
        .distinctUntilChanged(null, (x) => x.control.value)
        .map(x => { return { promise: validator(x.control), resolver: x.promiseResolver }; })
        .subscribe(
            (x) => x.promise.then(resultValue => x.resolver(resultValue),
            (e) => { console.log('async validator error: %s', e); }));
}

private _getValidator() {
    return (control) => {
        let promiseResolver;
        let p = new Promise((resolve) => {
            promiseResolver = resolve;
        });
        this._validate({ control: control, promiseResolver: promiseResolver });
        return p;
    };
}

static debounce(validator: (control: Control) => any, debounceTime = 400) {
    var asyncValidator = new this(validator, debounceTime);
    return asyncValidator._getValidator();
}
}

那么,在使用异步验证器的地方,你所要做的就是用这个调用包装你的验证器,并像往常一样编写你的验证器:

AsyncValidator.debounce(control => this.asyncValidator(control));

这是一个示例用法:

export class AppComponent {
form: ControlGroup;

constructor(private _formBuilder: FormBuilder) {
    var validator = AsyncValidator.debounce(control => this.asyncValidator(control));

    this.form = _formBuilder.group({
        name: ['', Validators.required, validator],
    });
}

asyncValidator(control): any {
    let p = new Promise(resolve => {
        // get from server information need to validate control

        if (control.value === 'valid value') {
            resolve(null);
        } else {

            resolve({
                asyncValidator: {
                    valid: false
                }
            });
        }
    });
    return p;
}
}

【讨论】:

    【解决方案2】:

    在 Angular 网站上有一个很棒的问题,它处理去抖动和 switchMapping 验证的问题:

    https://github.com/angular/angular/issues/6895

    这是我的工作解决方案(但所有功劳都归功于线程中的人)

    class AsyncValidator{
      private validatorInput: Subject<string>;
      private validatorChain: Observable<any>;
      constructor(service: ManageUsersService) {
        this.validatorInput = new Subject();
        this.validatorChain = this.validatorInput
          .debounceTime(400)
          .distinctUntilChanged()
          .switchMap(value => service.findUsersByName(value)
                                     .map(() => ({error: 'Error'})) //example of failed validation
                                     .catch(() => Observable.of(null))) //example of successful validation
          .do(v => console.log('mapped', v))
          .share()
          .take(1);
      }
    
      validate = (control: AbstractControl) => {
        // An immediate timeout is set because the next has to occur after the 
        // validator chain is subscribed to.
        setTimeout(() => this.validatorInput.next(control.value), 0);
        return this.validatorChain;
    }
    

    你可以这样使用它:

    this.createUserForm = fb.group({
            login: [ null, 
                     Validators.required, 
                     new AsyncValidator(userService).validate
                   ],
          });
        }
    

    【讨论】:

    • @charlie_pl 您可以使用 ReplaySubject 而不是 Subject 来确保在订阅验证器时该值存在。这样会更可靠。
    猜你喜欢
    • 2018-04-16
    • 2017-04-08
    • 2017-10-23
    • 2016-07-26
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多