angulartypescriptvalidationangular-reactive-formsvalidationerror

Name Duplication Validation


I trying in the following way a custom validation so that when a duplicate name is put in the input to the array of objects defined in the ts, the message that the name already exists in the mat-error should appear

This is my html where I implement the form with the name field, where there are 2 mat errors, one indicating that it is required and the other indicating that the name already exists, I would greatly appreciate your help

app.html

<form [formGroup]="form">
  <div>
      <mat-form-field>
        <input
         matInput
         type="text"
         [formControl]="name"
         required
        />
        <mat-error *ngIf="name.hasError('required')">
          It´s required
        </mat-error>
        <mat-error *ngIf="name.hasError('validateName')">
          the name already exists
        </mat-error>
      </mat-form-field>
   </div>
</form>

This is the ts where I initialize the form and put the validations

app.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, AbstractControl, ValidationErrors } from '@angular/forms';

@Component({
  selector: 'app-formulario',
  templateUrl: './component.html',
  styleUrls: ['./component.scss'],
})
export class AdminMaintainerFormsDialogComponent implements OnInit{
  public form: FormGroup;
  public name: AbstractControl;
  public totalStudents: any =
  [
    {
      name: "Sandro",
      rol: "student"
    },
    {
      name: "Paola",
      rol: "student"
    }
  ];

  constructor( private fb: FormBuilder) { }

  ngOnInit() {
    this.start();
  }

  start() {
    this.form = this.fb.group({
      name: ['', Validators.compose([Validators.required, this.validateName ])]
    });
  }

  private validateName(control: AbstractControl): ValidationErrors | null {
    const names = this.totalStudents;
    let compareName;

    names.map(value => {
      compareName = value.name;
    })

    return compareName.test(control.value) ? null : {
      validateName: {
        valid: false
      }
    };
  }

}

Solution

  • I believe that you are unable to retrieve totalStudents value in the validateName method.

    You need to implement a ValidatorFn method to pass the totalStudents.

    uniqueNameValidation(names: string[]): ValidatorFn {
      return (control: AbstractControl): ValidationErrors | null => {
        return !names.find((x) => x == control.value)
          ? null
          : {
              validateName: {
                valid: false,
              },
            };
      };
    }
    

    For the Form Group, pass the totalStudents by mapping with name only.

    this.form = this.fb.group({
      name: [
        '',
        Validators.compose([
          Validators.required,
          this.uniqueNameValidation(this.totalStudents.map((x) => x.name)),
        ]),
      ],
    });
    

    Sample StackBlitz Demo


    Side note:

    Validators.compose([
      Validators.required,
      this.uniqueNameValidation(this.totalStudents.map((x) => x.name)),
    ]),
    

    can be replaced with:

    [
      Validators.required,
      this.uniqueNameValidation(this.totalStudents.map((x) => x.name)),
    ]
    

    References

    Custom Validator with Parameters in Angular