<link rel="stylesheet" href="styles-3GBANQL6.css">
← Back to blog

Angular

Angular directive for form validation errors

Angular directive for form validation errors

Form validation is a critical aspect of any web application, ensuring that user inputs adhere to the expected format and preventing incorrect data submission. In Angular, you often need to display validation error messages dynamically, and in multi-language applications, providing these messages in the user's preferred language is essential.

Why Use a Custom Directive?

Directives in Angular are powerful tools for extending the behavior of DOM elements. Using a directive to handle form validation allows us to decouple error message handling from the form logic, making the code more modular and easier to maintain. Additionally, when used in combination with a translation service like Transloco, we can seamlessly handle multi-language support for error messages without duplicating code in components.

Directive Code: FormErrorDirective


  import { Directive, DoCheck, ElementRef, HostBinding, inject, Input, Renderer2 } from '@angular/core';
  import { NgControl } from '@angular/forms';
  import { TranslocoService } from '@ngneat/transloco';

    @Directive({
      selector: '[formControlName], [formControl]',
      standalone: true,
      })
    export class FormErrorDirective implements DoCheck {
    @Input() formControlName!: string;

    private elementRef: ElementRef = inject(ElementRef);
    private renderer: Renderer2 = inject(Renderer2);
    private translocoService: TranslocoService = inject(TranslocoService);

    private errorElement!: HTMLElement;

    constructor(private ngControl: NgControl) {}

    ngDoCheck(): void {
      this.updateErrorDisplay();
    }

    @HostBinding('class.form-element__error')
    get inValidClass(): boolean {
      return (this.ngControl.control?.touched && this.ngControl.control.invalid) || false;
    }

    private updateErrorDisplay(): void {
      const control = this.ngControl.control;
      if (control?.untouched && control.root.invalid) {
    return;
      }

      const errors = control?.errors;
      if (errors) {
    this.displayError(this.getErrorMessage(errors));
      } else {
    this.clearError();
      }
    }

    private getErrorMessage(errors: any): string {
      const firstErrorKey = Object.keys(errors)[0];
      return this.translocoService.translate(
        `${firstErrorKey}`,
        errors[firstErrorKey]
      );
    }

    private displayError(errorMessage: string): void {
      if (!this.errorElement) {
    this.createElement();
      }
      this.renderer.setProperty(this.errorElement, 'innerHTML', errorMessage);
      this.renderer.appendChild(
        this.elementRef.nativeElement.parentNode,
        this.errorElement
      );
    }

    private clearError(): void {
      if (this.errorElement) {
    this.renderer.removeChild(
      this.elementRef.nativeElement.parentNode,
      this.errorElement
    );
    this.errorElement = null!;
      }
    }

    private createElement(): void {
      this.clearError();
      this.errorElement = this.renderer.createElement('span');
      this.renderer.addClass(this.errorElement, 'form-element__error-message');
    }
  }
  

Step-by-Step Breakdown

@Directive Decorator

The @Directive decorator defines the directive and its selector, which targets form controls using formControlName or formControl.

NgControl and Form Control Binding

The NgControl allows us to access the form control's state (validity, touched, etc.), while @HostBinding applies a CSS class when the control is invalid and touched.

Error Handling

The ngDoCheck lifecycle hook is used to detect changes in the form control's validation state. The updateErrorDisplay function checks for errors, and if found, it translates and displays the corresponding error message.

Dynamic Translations

Using Transloco, we translate error messages based on the form validation keys (e.g., required, minlength, etc.) and their respective values (e.g., minimum length of characters).

DOM Manipulation with Renderer2

To safely manipulate the DOM, we inject Renderer2 and use it to create or remove error message elements in the form. This ensures the code remains efficient and compatible across different platforms and environments (such as server-side rendering).

Using the Directive

To use the FormErrorDirective in a form, apply it to any form control as follows:


  <form [formGroup]="form">
    <label for="username">Username</label>
    <input id="username" formControlName="username">
    !!-- Directive Automatically insert error code at this place 
when this form control has an invalid state 
and remove it when it is again valid  --!!
  </form>
  

Conclusion

By creating this custom directive, you can easily manage form validation errors in Angular while keeping your code modular and maintainable. Translating error messages dynamically with Transloco ensures your application is user-friendly in any supported language.

LET'S BUILD WHAT'S NEXT

Ready to turn your idea into a real solution?

Let's discuss how we can help you create value with technology.

Let's Talk