我的异步验证器如下:
asyncValidator(service:ApiCallsService):AsyncValidatorFn{
return (control:FormControl):Promise<ValidationErrors | null> | Observable<ValidationErrors | null> =>{
let timer$ = timer(2000);
return timer$.pipe(
take(1),
switchMap(()=> {
let videoId = service.checkUrl(control.value);
return service.getVideoDescription(videoId).toPromise().then((val:any)=>{
return (!val.id) ? {"invalidUrl": true} : null;
})
})
)
}
}
我的异步验证器的问题在于,添加到我的FormArray中的FormControl直到它们变得模糊为止,都不会自动显示其当前的“状态”。
这是我的FormArray和其中的FormControl:
<div class="url-con" formArrayName="urls" >
<div *ngFor="let url of urls.controls; let i=index" class="url-input-con">
<input minLength="5" placeholder="Video Url" class="url-input" [formControlName]="i">
<div class="url-pending" *ngIf="urls.controls[i].pending && !urls.controls[i].valid">Validating...</div>
</div>
</div>
出现带有“ url-pending”类的div,然后它不会消失-即使它依赖的FormControl已由后端验证-直到用户模糊div所依赖的FormControl。
与此类似的唯一其他堆栈溢出问题是link。我无法完全理解该链接的说明,与链接发布者相比,我还感到比较复杂的一点是,我在表单中有一个加号的图标,以便用户可以向FormArray添加更多FormControl。我无法弄清楚如何向用户通过与表单交互添加的FormControl添加指令。
我将回答自己的问题,因为我想出了解决该问题的方法,但是我以一种“骇人”的方式解决了它。如果其他人对此有更好的答案,请回复。
答案 0 :(得分:0)
我在formArray(#formArray)中添加了一个标识符:
<div #formArray class="url-con" formArrayName="urls" >
<div *ngFor="let url of urls.controls; let i=index" class="url-input-con">
<input minLength="5" placeholder="Video Url" class="url-input" [formControlName]="i">
<div class="url-pending" *ngIf="urls.controls[i].pending && !urls.controls[i].valid">Validating...</div>
</div>
</div>
然后我将finalize()添加到异步验证器中的timer $的返回中。在运算符的回调内部,我使FormArray的每个FormControl成为焦点,然后进行模糊处理。
asyncValidator(service:ApiCallsService):AsyncValidatorFn{
return (control:FormControl):Promise<ValidationErrors | null> | Observable<ValidationErrors | null> =>{
let timer$ = timer(2000);
return timer$.pipe(
take(1),
switchMap(()=> {
let videoId = service.checkUrl(control.value);
return service.getVideoDescription(videoId).toPromise().then((val:any)=>{
return (!val.id) ? {"invalidUrl": true} : null;
})
}),
finalize(()=>{
Array.from(this.formArray.nativeElement.children)
.forEach((val:HTMLElement,ind)=>{
(Array.from(val.children)[0] as HTMLElement).focus();
(Array.from(val.children)[0] as HTMLElement).blur();
})
})
)
}
}
每个FormControl都必须首先集中精力,因为如果用户在验证结束之前模糊不清,则FormControl永远不会模糊并且“待定”状态将永远持续显示(尽管不起作用)。
答案 1 :(得分:0)
我遇到了类似的问题;但是,我可以通过将statusChanges
输送到async
{{field.statusChanges | async}}
or
...*ngIf="(field.statusChanges | async) === 'PENDING'"...
所以在您的情况下:
<div #formArray class="url-con" formArrayName="urls" >
<div *ngFor="let url of urls.controls; let i=index" class="url-input-con">
<input minLength="5" placeholder="Video Url" class="url-input" [formControlName]="i">
<div class="url-pending"
*ngIf="(urls.controls[i].statusChanges | async) === 'PENDING'">
Validating...
</div>
</div>
</div>
答案 2 :(得分:0)
这与表单的touched
状态有关。在控件为untouched
之前,它被视为“也许用户尚未完成输入”。
所以您对症状几乎是正确的,但是还有一种更简单的更好的方式来“触摸”控件。
关键部分是使用默认的ReactiveForm API来使控件感动。在我的finalize
部分中查看效果如何:
component.ts
public emailControl = new FormControl('',
/* sync */
[
control => control.value && /.+@.+/.test(control.value) ? null : { invalidEmail: true },
],
/* async */
[
control => control.value ? this.debouncedCheck(control.value) : of(null)
]
);
private debouncedCheck(value): Observable<ValidationErrors> {
// debounce (although it will be PENDING even during those 500ms!!)
return timer(500).pipe(
// Handle validation
switchMap( () => this.doAsyncCheck(value)),
tap(checkResponse => /* ... handle validation response ... */),
catchError( error => /* ... handle runtime errors ... */),
// "Touch" the control since its data was already sent and used somewhere
finalize(() => this.lookupEmail.markAsTouched()),
);
}
template.html
<mat-form-field>
<mat-label>Find by email</mat-label>
<input matInput [formControl]="emailControl">
<mat-spinner matSuffix *ngIf="emailControl.pending" [diameter]="15"></mat-spinner>
<mat-error *ngIf="emailControl.hasError('invalidEmail')">Email is invalid</mat-error>
<mat-error *ngIf="emailControl.hasError('noRecords')">No records found for email</mat-error>
</mat-form-field>
在尝试实施简单的/.+@.+/
电子邮件验证时,我个人遇到了此问题。
问题是输入不应该对用户“ INVALID !!”发出尖叫。在第一个字母写入后。它应该等到输入大概是“完成”:用户继续到下一个输入字段(邮政地址,昵称等)时。
当用户离开控件时,它将获得touched: true
并呈现所有验证(它仍在后台进行计算,但由于touched: false
状态而未完全呈现)。现在,用户可以返回到触摸的输入(并保持触摸状态,直到手动重置状态!),并且可以实时查看所有验证。
在上面的示例中,控件在它不是“有效”电子邮件之前是无效的,甚至没有尝试执行异步检查(因为同步失败),但是在控制它时touched: false
不显示它。
但是,当最终something@like.this
触发异步检查时,将显示“待处理操作” -spinner,控件本身变为touched: true
,并根据解决方案显示验证结果。