What do I have to return in the customerNameValidator if the
async validation fails/succeeds that my 'customerName' FormControl is invalid?
this.customerForm = this.formBuilder.group({
customerName:
[this.newCustomerName, [Validators.minLength(2), Validators.required],[this.customerNameValidator.bind(this)]]
});
customerNameValidator(c: AbstractControl)
{
return this.service.customerExists(c.value,this.companyId).subscribe(response =>
{
if(response == true)
{
alert("true");
}
else
{
alert("false");
}
});
}
Rather than subscribing, you should map the observable to change the result of the returning stream, rather than reading from it.
customerNameValidator(c: AbstractControl)
{
return this.service.customerExists(c.value,this.companyId).map(response =>
{
if(response == true)
{
return { customerExists: true };
}
else
{
return;
}
});
}
Returning an object with a value that is true is how you should return the observable. You may be missing some important steps for async validators though, but because we don't gave all your code it's hard to say. Try checking out this article or this article for more information.
I implemented an reactive form with an AsyncValidatorFn on angular 6.1.1. and would like to share some of my learning
I found out that angular does not (automatically) update the form control for AsyncValidatorFn as it does for internal sync validators .
so, as per the "AsyncValidatorFn" interface spec, you have to "manually" update your form control in your implementation of
(c: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null>;
and then, you will check the control state in the html element
What I had implemented is an Username existence checking that might be very commonly found in an user sign-up process
below are the code excerpts:
the form control
// Supports alphabets and numbers no special characters except underscore('_') and dash('-') min 3 and max 20 characters.
this.userName = new FormControl('', Validators.compose([Validators.required, Validators.pattern('^[A-Za-z0-9_-]{3,20}$')]),Validators.composeAsync([this.checkUser()]));
the custom async validator and the helper functions
checkUser (): AsyncValidatorFn{
return (c: AbstractControl): Observable<ValidationErrors> => {
return c
.valueChanges
.debounceTime(400)
.mergeMap(value => this.gabriel.filter({'userName':value}))
.map(stat => this.mapErr(c, stat));
}
}
private mapErr(c: AbstractControl, res: any): ValidationErrors{
let err: ValidationErrors;
switch (res['state']){
case 0:
err = null;
break;
case -100:
err = {'existed': true};
break;
case -1:
default:
err = {'failed': true};
}
c.setErrors(err);
return err;
}
note that I input the control as a parameter into the "mapErr" function, and set the control by "c.setErrors(err);".
the "return err;" statement return "ValidationErrors" as per the "AsyncValidatorFn" interface spec.
the "gabriel.filter()"queries the backend with the extracted username; and returns 0, -100, -1 respectively for "ok", "duplicated", and "operation failed"
filter(json): Observable<{}>{
let body = JSON.stringify(json);
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({ headers: headers });
return this.http.post(Cons.filter, body, options).timeout(10000).map((res:Response) => res.json());
}
the control checking in html file
<form [formGroup]="sf" (ngSubmit)="signin()">
<ion-item>
<ion-label>UserName</ion-label>
<ion-input type="text" formControlName="userName" [class.invalid]="userName.dirty&&userName.invalid&&userName.errors!=null" ></ion-input>
</ion-item>
<p *ngIf="userName.dirty && userName.hasError('existed')">
Username already existed
</p>
<p *ngIf="userName.dirty && userName.hasError('failed')">
can not check validity of Username
</p>
I also found out that the async validators are not trigger until sync validator are satisfied in one form control.
in my case, I also used the built-in Validators.pattern to define a minimum length of 3.(see above Username formControl definition)
the custom async validator never triggers as long as my input length is shorter than 3.