Работа с Angular

Интеграция Validator.js в Angular строится вокруг расширения стандартной системы валидации форм через ReactiveForms и Template-driven forms, где библиотека используется как слой низкоуровневых проверок строковых значений. Основная идея заключается в том, что Angular отвечает за жизненный цикл и состояние форм, а Validator.js — за детальную проверку значений.

Библиотека устанавливается стандартным способом через npm:

npm install validator

После установки функции подключаются точечно, чтобы не перегружать бандл:

import isEmail from 'validator/lib/isEmail';
import isURL from 'validator/lib/isURL';
import isLength from 'validator/lib/isLength';
import isAlphanumeric from 'validator/lib/isAlphanumeric';

Такой подход предпочтительнее импорта всей библиотеки целиком, поскольку Angular-приложения чувствительны к размеру итогового bundle.

Базовая модель интеграции с Reactive Forms

Reactive Forms используют объект FormControl, который принимает массив валидаторов:

import { FormControl, Validators } from '@angular/forms';

const emailControl = new FormControl('', [
  Validators.required
]);

Validator.js подключается через создание кастомных функций:

import { AbstractControl, ValidationErrors } from '@angular/forms';
import isEmail from 'validator/lib/isEmail';

export function emailValidator(control: AbstractControl): ValidationErrors | null {
  const value = control.value;

  if (!value) {
    return null;
  }

  return isEmail(value) ? null : { emailInvalid: true };
}

Использование в форме:

const emailControl = new FormControl('', [
  Validators.required,
  emailValidator
]);

Принцип построения кастомных валидаторов

Angular ожидает, что валидатор возвращает либо null, либо объект ошибок. Validator.js возвращает boolean, поэтому адаптация заключается в преобразовании результата.

Шаблон универсального валидатора:

export function createValidator(
  validatorFn: (value: string) => boolean,
  errorKey: string
) {
  return (control: AbstractControl): ValidationErrors | null => {
    const value = control.value;

    if (value === null || value === undefined || value === '') {
      return null;
    }

    return validatorFn(value) ? null : { [errorKey]: true };
  };
}

Пример использования:

import isURL from 'validator/lib/isURL';

export const urlValidator = createValidator(isURL, 'invalidUrl');

Набор типовых валидаторов на Validator.js

Проверка email

import isEmail from 'validator/lib/isEmail';

export const emailValidator = createValidator(isEmail, 'emailInvalid');

Проверка URL

import isURL from 'validator/lib/isURL';

export const urlValidator = createValidator(
  (value) => isURL(value, { require_protocol: true }),
  'urlInvalid'
);

Проверка длины строки

import isLength from 'validator/lib/isLength';

export const minLengthValidator = (min: number) =>
  createValidator(
    (value) => isLength(value, { min }),
    'minLengthInvalid'
  );

Проверка алфавитно-цифрового состава

import isAlphanumeric from 'validator/lib/isAlphanumeric';

export const alphanumericValidator =
  createValidator(isAlphanumeric, 'notAlphanumeric');

Комбинирование Angular Validators и Validator.js

Angular позволяет объединять встроенные и сторонние проверки:

const usernameControl = new FormControl('', [
  Validators.required,
  Validators.minLength(3),
  alphanumericValidator
]);

Такое разделение разгружает логику: Angular выполняет структурные проверки, Validator.js — семантические.

Работа с пустыми значениями и нормализация

Validator.js чаще всего ожидает строку. Angular же может передавать null. Поэтому нормализация становится обязательной частью слоя адаптации:

const value = (control.value ?? '').toString().trim();

Игнорирование этого шага приводит к ложным ошибкам при первичной инициализации формы.

Cross-field валидация с использованием Validator.js

Validator.js можно использовать и в проверках нескольких полей:

import { FormGroup, ValidationErrors } from '@angular/forms';
import isNumeric from 'validator/lib/isNumeric';

export function priceRangeValidator(group: FormGroup): ValidationErrors | null {
  const min = group.get('min')?.value;
  const max = group.get('max')?.value;

  if (!isNumeric(min) || !isNumeric(max)) {
    return { notNumeric: true };
  }

  return Number(min) <= Number(max)
    ? null
    : { rangeInvalid: true };
}

Применение:

const form = new FormGroup(
  {
    min: new FormControl(''),
    max: new FormControl('')
  },
  { validators: priceRangeValidator }
);

Использование в Template-driven forms

В шаблонных формах интеграция выполняется через директивы:

import { Directive } from '@angular/core';
import { NG_VALIDATORS, Validator, AbstractControl } from '@angular/forms';
import isEmail from 'validator/lib/isEmail';

@Directive({
  selector: '[emailValidator]',
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: EmailValidatorDirective,
      multi: true
    }
  ]
})
export class EmailValidatorDirective implements Validator {
  validate(control: AbstractControl) {
    const value = control.value;

    if (!value) {
      return null;
    }

    return isEmail(value) ? null : { emailInvalid: true };
  }
}

Использование в HTML:

<input name="email" ngModel emailValidator />

Централизация валидаторов через сервис

При масштабировании приложения валидаторы выносятся в отдельный слой:

@Injectable({ providedIn: 'root' })
export class ValidationService {
  email = emailValidator;
  url = urlValidator;

  minLength(min: number) {
    return minLengthValidator(min);
  }
}

Это упрощает повторное использование и унифицирует правила.

Типизация и строгий контроль ошибок

Angular допускает строгую типизацию ошибок:

interface EmailError {
  emailInvalid: true;
}

type EmailValidationErrors = EmailError | null;

Это позволяет строить предсказуемую модель обработки ошибок в UI-слое.

Оптимизация производительности

При частой валидации (например, ввод в реальном времени) важны следующие ограничения:

  • использование точечных импортов validator/lib/...
  • отсутствие лишних преобразований строк
  • минимизация сложных регулярных проверок внутри кастомных валидаторов
  • кэширование результатов при тяжелых вычислениях

Особенности поведения Angular Forms

Angular повторно вызывает валидаторы при каждом изменении состояния FormControl. Это делает критичным отсутствие побочных эффектов внутри функций валидации. Validator.js в этом контексте подходит как чистый функциональный слой, поскольку не изменяет входные данные и не хранит состояние.

Обработка ошибок в UI-слое

Результат валидации формируется в объект:

{ emailInvalid: true }

Доступ к ошибке:

emailControl.errors?.['emailInvalid']

Такой формат позволяет унифицировать отображение сообщений об ошибках независимо от источника проверки.

Повторное использование логики между Angular и сервером

Validator.js часто применяется как общий слой валидации между фронтендом и Node.js API. Это снижает расхождение правил проверки данных на клиенте и сервере, особенно в случаях email, URL, длины строк и числовых диапазонов.