Skip to content

Latest commit

 

History

History
109 lines (91 loc) · 2.87 KB

README.md

File metadata and controls

109 lines (91 loc) · 2.87 KB

Angular custom form error message


Sources:


Custom error message stored in the customErrorMessages:

const customErrorMessages: Record<string, string> = {
  required: 'Required field',
  email: 'It is not a valid email',
  minlength: 'It is too short',
  maxlength: 'It is too long',
}

To get them in the component, create a custom injection token:

const customErrorMessages: Record<string, string> = {
  required: 'Required field',
  email: 'It is not a valid email',
  minlength: 'It is too short',
  maxlength: 'It is too long',
}

Error message component implements ControlValueAccessor, injects NgForm and custom error messages from the CUSTOM_ERRORS. Injection the DestroyRef necessary for unsubscribing via pipe. Methods writeValue, registerOnChange, registerOnTouched and setDisabledState have to be implemented but don't contain any logic.

When an error occurs in Validator, it will be handled in this component.

export class FormErrorMessageComponent implements ControlValueAccessor, OnInit {
  private readonly control = inject(NgControl, {self: true});
  private readonly errors = inject(CUSTOM_ERRORS);
  private destroyRef = inject(DestroyRef);

  protected readonly error = signal('')

  constructor() {
    this.control.valueAccessor = this;
  }

  ngOnInit(): void {
    this.control.control?.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
      if (this.control.invalid) {
        const errorType = Object.keys(this.control.errors || {})?.[0];
        this.error.set(this.errors[errorType])
      } else {
        this.error.set('')
      }
    })
  }

  writeValue(obj: any): void {
  }

  registerOnChange(fn: any): void {
  }

  registerOnTouched(fn: any): void {
  }

  setDisabledState?(isDisabled: boolean): void {
  }

}

Use custom error messages in the component:

@Component({
  // all other entries
  providers: [
    {provide: CUSTOM_ERRORS, useValue: customErrorMessages},
  ],
})
export class SomeComponent implements OnInit {
  constructor(
    private fb: FormBuilder,
  ) {
  }

  form: FormGroup;

  ngOnInit(): void {
    this.form = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      length: ['', [Validators.minLength(3), Validators.maxLength(10)]],
    });
  }
}

Template:

<form [formGroup]="form">
  <div class="form-control">
    <label for="email">Email</label>
    <input formControlName="email" id="email" type="email" name="email">
    <app-form-error-message formControlName="email"></app-form-error-message>
  </div>

  <div class="form-control">
    <label for="length">Text</label>
    <input formControlName="length" id="length" type="text" name="length">
    <app-form-error-message formControlName="length"></app-form-error-message>
  </div>
</form>