Skip to content

useControlErrors

Returns the validation errors of an AbstractControl as a signal. The signal updates reactively whenever the control's validation errors change. Works with FormControl, FormGroup, and FormArray.

Usage

typescript
import { useControlErrors } from 'ng-reactive-utils';

@Component({
  template: `
    <input [formControl]="emailControl" placeholder="Email" />

    @if (errors()?.['required']) {
      <span class="error">Email is required</span>
    }

    @if (errors()?.['email']) {
      <span class="error">Please enter a valid email address</span>
    }

    @if (errors()?.['minlength']) {
      <span class="error">
        Email must be at least {{ errors()?.['minlength'].requiredLength }} characters
      </span>
    }
  `,
})
class EmailFieldComponent {
  emailControl = new FormControl('', [
    Validators.required,
    Validators.email,
    Validators.minLength(5),
  ]);

  errors = useControlErrors(this.emailControl);
}

Advanced Usage

typescript
import { useControlErrors } from 'ng-reactive-utils';

@Component({
  template: `
    <input [formControl]="passwordControl" type="password" />

    <ul class="validation-list">
      @for (error of errorMessages(); track error) {
        <li class="error">{{ error }}</li>
      }
    </ul>
  `,
})
class PasswordFieldComponent {
  passwordControl = new FormControl('', [
    Validators.required,
    Validators.minLength(8),
    Validators.pattern(/[A-Z]/),
    Validators.pattern(/[0-9]/),
  ]);

  errors = useControlErrors(this.passwordControl);

  errorMessages = computed(() => {
    const errors = this.errors();
    if (!errors) return [];

    const messages: string[] = [];
    if (errors['required']) messages.push('Password is required');
    if (errors['minlength']) messages.push('Must be at least 8 characters');
    if (errors['pattern']) messages.push('Must contain uppercase and numbers');
    return messages;
  });
}

Parameters

ParameterTypeDefaultDescription
controlAbstractControlrequiredThe control to get errors from

Returns

Signal<ValidationErrors | null> - A readonly signal containing the validation errors or null if no errors

Common Error Types

ErrorDescription
requiredValue is empty
emailValue is not a valid email
minlengthValue is shorter than required
maxlengthValue is longer than allowed
patternValue doesn't match the pattern
minNumeric value is below minimum
maxNumeric value is above maximum

Notes

  • Works with FormControl, FormGroup, and FormArray
  • Uses toSignal with control.statusChanges observable
  • Returns null when the control has no validation errors
  • Error object keys correspond to validator names

Source

ts
import { Signal } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { AbstractControl, ValidationErrors } from '@angular/forms';
import { map } from 'rxjs';

/**
 * Returns the validation errors of an AbstractControl as a signal.
 * The signal updates reactively whenever the control's validation errors change.
 * Works with FormControl, FormGroup, and FormArray.
 *
 * @param control - The AbstractControl to get errors from
 * @returns A signal containing the validation errors or null if no errors
 *
 * @example
 * ```typescript
 * @Component({
 *   template: `
 *     <input [formControl]="emailControl" />
 *     @if (errors()?.['required']) {
 *       <span>Email is required</span>
 *     }
 *     @if (errors()?.['email']) {
 *       <span>Please enter a valid email</span>
 *     }
 *   `
 * })
 * class MyComponent {
 *   emailControl = new FormControl('', [Validators.required, Validators.email]);
 *   errors = useControlErrors(this.emailControl);
 * }
 * ```
 */
export const useControlErrors = (control: AbstractControl): Signal<ValidationErrors | null> => {
  return toSignal(control.statusChanges.pipe(map(() => control.errors)), {
    initialValue: control.errors,
  }) as Signal<ValidationErrors | null>;
};

Released under the MIT License.