角度异步验证器FormControl直到模糊才更新

时间:2019-06-10 11:28:24

标签: angular typescript angular-forms

我的异步验证器如下:

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添加指令。

我将回答自己的问题,因为我想出了解决该问题的方法,但是我以一种“骇人”的方式解决了它。如果其他人对此有更好的答案,请回复。

3 个答案:

答案 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,并根据解决方案显示验证结果。