typescript - Angular asynkron validator FormControls uppdateras inte förrän de tappar fokus
Min asynkrona validator ser ut så här:
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;
})
})
)
}
}
Problemet med min asynkrona validator är att mina FormControls som läggs till i min FormArray inte uppfattar sitt nuvarande 'status' förrän de förlorar fokus.
Detta är min FormArray och min FormControl inuti den:
<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>
Diven med klassen "url-pending" visas, och sedan försvinner den inte - även om FormControl som den beror på valideras av backend - förrän användaren lämnar FormControl som divan beror på.
Den enda andra Stack Overflow-frågan som liknar denna är denna länk. Jag kunde inte fullt ut förstå instruktionerna i den länken, och en ytterligare komplikation jag hade jämfört med personen som postade länken är att jag har en ikon i mitt formulär formad som ett plustecken så att användaren kan lägga till fler FormControls till FormArray. Jag kunde inte lista ut hur man lägger till direktiv till de FormControls som användaren lade till genom att interagera med formuläret.
Jag kommer att svara på min egen fråga eftersom jag kom på hur man löser denna fråga, men jag löste den på ett 'hackigt' sätt. Om någon annan vet ett bättre svar på detta, vänligen svara.
Svar #1
Jag lade till en identifierare på 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>
Sedan lade jag till finalize() på returvärdet av timer$i Async Validator. Inuti callbacken för operatören gjorde jag så att varje FormControl i FormArray fick fokus och sedan förlorade fokus.
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();
})
})
)
}
}
Varje FormControl måste fokuseras först eftersom om användaren lämnar fokus innan valideringen är klar, så lämnar FormControl aldrig fokus och tillståndet 'pending' fortsätter att visas för evigt (inte funktionellt dock).
Svar #2
Jag stötte på ett liknande problem; dock kunde jag lösa det genom att koppla statusChanges till async
{{field.statusChanges | async}}
or
...*ngIf="(field.statusChanges | async) === 'PENDING'"...
Så i ditt fall:
<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>
Källor
- https://angular.io/guide/form-validation#async-validation
