Angular 表单控件异步验证问题
我试图在Angular2上实现一个异步验证,但是当我出于某种原因试图输入一些东西时,去抖动不起作用。它会根据我的输入触发多次。此外,在加载时,将触发我的异步验证器,从而将状态设置为“挂起”,以便信息框显示Angular 表单控件异步验证问题,angular,observable,angular2-observables,Angular,Observable,Angular2 Observables,我试图在Angular2上实现一个异步验证,但是当我出于某种原因试图输入一些东西时,去抖动不起作用。它会根据我的输入触发多次。此外,在加载时,将触发我的异步验证器,从而将状态设置为“挂起”,以便信息框显示正在检查… 下面是我如何实现它的: HTML: 异步验证代码: couponAsyncValidation(control: FormControl): Promise<any | null> | Observable<any | null> { return
正在检查…
下面是我如何实现它的:
HTML:
异步验证代码:
couponAsyncValidation(control: FormControl): Promise<any | null> | Observable<any | null> {
return new Promise( (res, rej) => {
control.valueChanges.pipe(
debounceTime(2000),
distinctUntilChanged(),
switchMap(value => this.userService.couponChecker(value)),
map( (q) => q ),
first()
).subscribe(
(d) => {
console.log(d)
d.is_coupon_valid ? res(null) : res({invalid: true})
}
)
})
}
我有一个类似的情况,除了我的异步验证器直接在表单上。。。代码如下所示,但与您的需要不完全匹配。。我怀疑您的核心问题是,您对控件值的订阅发生了更改,这是您不需要做的,因为将其设置为验证器应该已经导致在值更改时调用该验证器
validateExists(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
{
return timer(500).pipe(
switchMap(() =>
this.service.checkThingExists(control.value.value1, control.value.value2).pipe(
tap(() => setTimeout(() => this.isValid.emit(this.form.get('parcel').valid))),
map(valid => (valid ? null : { notFound: true }))
)
)
);
}
validateExists(控件:AbstractControl):承诺|可观察{
{
返回计时器(500)。管道(
开关映射(()=>
this.service.checkThingExists(control.value.value1,control.value.value2)。管道(
点击(()=>setTimeout(()=>this.isValid.emit(this.form.get('packet').valid)),
映射(valid=>(valid?null:{notFound:true}))
)
)
);
}
我有一个类似的情况,除了我的异步验证器直接在表单上…下面是代码,但它不会完全符合您的需要。我怀疑您的核心问题是,您订阅的控制值发生了更改,而您不需要这样做,因为将其设置为验证器应该已经导致该更改生效无论值何时更改,都要调用dator
validateExists(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
{
return timer(500).pipe(
switchMap(() =>
this.service.checkThingExists(control.value.value1, control.value.value2).pipe(
tap(() => setTimeout(() => this.isValid.emit(this.form.get('parcel').valid))),
map(valid => (valid ? null : { notFound: true }))
)
)
);
}
validateExists(控件:AbstractControl):承诺|可观察{
{
返回计时器(500)。管道(
开关映射(()=>
this.service.checkThingExists(control.value.value1,control.value.value2)。管道(
点击(()=>setTimeout(()=>this.isValid.emit(this.form.get('packet').valid)),
映射(valid=>(valid?null:{notFound:true}))
)
)
);
}
异步验证器在每次值更改时都会被调用。因此,每次值更改时,您都会创建一个新的observable并订阅它。每次值更改时,这个observable都会发出信号。@jbnize我还不知道。如果我的问题不清楚,很抱歉。但是为什么会发生这种情况,即使我设置了去BounceTime
在设置的时间过去之前阻止它施放EDI错过了对first()的调用。每次值更改时都会调用异步验证器。因此,每次值更改时,您都会创建一个新的observable并订阅它。每次值更改时,该observable都会发出信号。@jbnize我已经知道了。如果我的问题不清楚,很抱歉。但为什么会发生这种情况,即使我设置了debounceTime
我错过了对first()的调用。如果我不使用管道传输值更改,那么在用户键入时,您将如何从用户处获取值或输入?验证函数的输入参数具有具有该值的控件。因此,您只需执行control.value。您的函数将在每次输入更改时被调用,因为您将其设置为验证程序。因此,您不需要添加任何值捕获值更改的另一层。我明白了。这是第一个解决方案。但由于这是异步验证,每次键盘点击或输入时,我都会调用http请求。因此,我尝试使用valueChanges
,因为它是可观察的,所以我可以通过管道将其传输,并从rxjs添加一个debounceTime运算符,以防止重新请求在我上面发布的示例中,我使用的是计时器(500)为了实现类似的功能,我仍然不需要订阅valueChanges。Timer也是一个RxJs函数。如果我不使用管道传输值更改,那么在用户键入时,您将如何从用户处获取值或输入?验证函数的输入参数具有具有具有值的控件。因此,您只需执行control.value。您的函数是每次输入更改时都会被调用,因为您将其设置为验证器。因此,您不需要添加另一个捕获值更改的层。我明白了。这是第一个解决方案。但由于这是异步验证,因此每次键盘点击或输入时,我都会调用http请求。因此,我尝试使用valueChanges
because它是一个可观察的,所以我可以通过管道从rxjs添加一个去BounceTime操作符,这样每次键盘被点击时它都会阻止请求。在我上面发布的示例中,我使用timer(500)来实现类似的功能,我仍然不需要订阅值更改。timer也是rxjs函数。
import { AbstractControl, AsyncValidatorFn } from '@angular/forms';
import { switchMap, map} from 'rxjs/operators';
import { UserService } from '../user/services/user.service';
import { Observable, timer, of } from 'rxjs';
export class AsyncValidator {
static couponValidator(miliSec: number, service: UserService): AsyncValidatorFn {
return (control: AbstractControl): Observable<any | null> => {
return timer(miliSec).pipe(
switchMap(() => {
if ( control.value ) {
return service.couponChecker(control.value);
}
// this one is needed because for some reason on loading, the signup page
// will immediately trigger this async call which sends a request to the backend
// even though user has not inputted anything.
// then sets the status of the form to pending thus resulting to an invalid form
return of({is_coupon_valid: true});
}),
map( (res) => res.is_coupon_valid ? null : res )
);
};
}
}
validateExists(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
{
return timer(500).pipe(
switchMap(() =>
this.service.checkThingExists(control.value.value1, control.value.value2).pipe(
tap(() => setTimeout(() => this.isValid.emit(this.form.get('parcel').valid))),
map(valid => (valid ? null : { notFound: true }))
)
)
);
}