从晚上开始,我在augular2中玩过表单验证。
所有基本案例都易于实现,并且工作正常,但我坚持使用异步验证。我创建了一个非常小的例子http://plnkr.co/edit/Xo8xwJjhlHkXrunzS8ZE但它没有用。
根据测试"应该在状态更新为待决之后触发事件"来自model_spec.ts通过创建控制组进行注册假设以某种方式工作
builder.group({login: ["",Validators.required,validationFuctionWhichReturnsPromise]
我花了整整一个晚上发现这个代码已经在alfa-46中发布了(我使用了alfa-45),并且在更新依赖项之后,异步验证开始起作用。该功能非常新鲜,并未完整记录,但
(对于尚未尝试过的人)基本上异步验证器是一个具有Control参数并返回验证结果的promise的函数。注册验证器有两种方法。 1)我在我的例子中使用的那个和2)作为通过NG_ASYNC_VALIDATORS提供验证器的指令(参见UniqLoginValidator和NgFormControl以了解它是如何工作的)。您可以编写多个验证程序(尚未测试,但执行此操作的函数在代码中,请参阅https://github.com/angular/angular/commit/cf449dd)。
但是当我最终到达并运行验证器时,一个新的问题就到了。异步验证器非常适合在服务器端验证中使用它。但是在每个keyup之后每次更改model.fe之后都会调用验证。因此,如果我们将在每个密钥启动后向服务器发送请求,那么它将不会太有效;)我检查了它是如何在角度1中完成的,并且它们可以去除验证事件。
我的问题是:
我正在考虑使用debounce(来自underscorejs)来扭曲验证器函数,但它不起作用,因为angular希望每次都获得一个有效的promise。
我的第二个问题是,如果所有事件都使用RxJs,那么也许我可以在负责验证的事件流上应用去抖动。在model.ts中,从验证器返回的promise是更改为observable并添加了新订阅。我们没有任何障碍物(Observable)可以在那里进行去抖动。
我在How to trigger Form Validators in angular2
中发现了一个密切相关的问题PS还有与异步验证器相关的其他问题,它仍处于打开状态https://github.com/angular/angular/issues/1068
答案 0 :(得分: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();
}
}
然后你要做的就是使用异步验证器只需用这个调用包装你的验证器,并按照通常的方式写你的验证器:
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;
}
}
答案 1 :(得分:2)
角网站上有一个很棒的问题,它解决了debouncing和switchMapping验证的问题:
https://github.com/angular/angular/issues/6895
这是我的工作解决方案(但所有的功劳都来自线程的人)
Vec3<float> Image::getPixel(unsigned int x, unsigned int y) const {
math::Vec3<float> v;
if (x < getWidth() && y < getHeight() && x >= 0 && y >= 0)
{
int s = 3 * y * getWidth() + 3 * x;
v[RED] = buffer[s + RED];
v[GREEN] = buffer[s + GREEN];
v[BLUE] = buffer[s + BLUE];
}
return v;
}
你这样使用它:
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;
}