一般:angular2中的异步验证

prz*_*cio 4 validation asynchronous angular

从晚上开始,我在augular2中玩过表单验证.

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

根据测试"应该在状态已经更新为挂起之后触发事件"来自model_spec.ts通过创建控制组进行注册假设以某种方式工作

builder.group({login: ["",Validators.required,validationFuctionWhichReturnsPromise]
Run Code Online (Sandbox Code Playgroud)

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

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

但是当我最终达到并运行验证器时,一个新的问题就到了.异步验证器非常适合在服务器端验证中使用它.但是在每个keyup之后每次更改model.fe之后都会调用验证.因此,如果我们将在每个密钥启动后向服务器发送请求,那么它将不会太有效;)我检查了它是如何在角度1中完成的,并且它们可以去除验证事件.

我的问题是:

  1. 如何使用异步验证器实现节流或去抖?我看到了一些想法,但没有一个是好的(主要是因为他们需要改变角度代码本身).有没有有效的方法来做到这一点,而无需等待新的角度释放?

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

我的第二个问题是,如果所有事件都使用RxJs,那么也许我可以在负责验证的事件流上应用debounce.在model.ts中,从验证器返回的promise是更改为observable并添加了新订阅.我们没有任何障碍物(Observable)可以在那里申请去抖.

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

我在如何触发angular2中的表单验证器时发现了一个密切相关的问题

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

小智 9

这是一个帮助程序类,您可以使用它来去除所有异步验证程序:

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();
}
}
Run Code Online (Sandbox Code Playgroud)

那么你所要做的就是使用异步验证器只需用这个调用包装你的验证器并按照你通常的方式写你的验证器:

AsyncValidator.debounce(control => this.asyncValidator(control));
Run Code Online (Sandbox Code Playgroud)

以下是一个示例用法:

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;
}
}
Run Code Online (Sandbox Code Playgroud)