angularvalidationasynchronousangular-custom-validators

Angular async validator check unicity of input in database


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.


Solution

  • 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>