I have an <input>
where the user enter a value that is supposed to be unique in database. I want to check dynamically in the front-end if that value is unique or not.
I want also to display an error message if the value is already stored in database.
You can use a custom async validator to do that. Let's say you want to check that the name is unique. First, create a new file for the async validator :
@Injectable({ providedIn: 'root' })
export class NameValidator {
constructor(private nameService: NameService) { }
CheckNameValidator(): AsyncValidatorFn {
return (control: AbstractControl): Observable<{ [key: string]: boolean } | null> => {
return this.nameService.nameExists(control.value)
.pipe(
map(valueExists => {
if (valueExists) {
return { nameExists: true };
}
return null;
}
)
)
}
}
The validator is a service where you import the service nameService where you can call your api. The api must return if the value is present or not. Then, in the controller :
export class MainComponent implements OnInit {
constructor(
private fb: FormBuilder,
private nameValidator: NameValidator
) {}
public formGroup: FormGroup;
ngOnInit(): void {
this.formGroup = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)], [this.nameValidator.CheckNameValidator()]]
}, {updateOn: 'blur'});
}
When you build your formgroup, each form control can hold a value, an array of validators, and an array of async validators. That's where i put the call to the asyn validator service.
Notice the {updateOn: 'blur'}
, if you do not write this, the update to check the validators is on change, so you make way too many api calls. With onBlur the api call is done onblur => performance gain.
If you use angular material, your view should look something like this :
<ng-container [formGroup]="formGroup">
<mat-form-field class="field-full-width">
<input matInput placeholder="Name" formControlName="name">
<mat-error *ngIf="formGroup.controls['name'].errors?.nameExists">This name is already in use</mat-error>
</mat-form-field>
</ng-container>