Создание кастомных валидаторов в Angular Forms

Валидация форм — одна из самых важных частей фронтенд-разработки. Особенно это актуально для приложений, реализованных с помощью Angular, где правильная валидация обеспечивает не только корректность данных, но и высокое качество пользовательского опыта. Angular содержит широкий набор встроенных валидаторов, таких как required, minLength, pattern и другие, но реальный бизнес-логика часто требует более сложных и специфических правил проверки данных. В таких случаях необходимы кастомные валидаторы. Их создание и внедрение — важный навык для любого разработчика Angular.

В этой статье мы подробно рассмотрим, как создавать и использовать собственные валидаторы в Angular Forms, разберем типичные примеры, а также плюсы, минусы и лучшие практики кастомной валидации.

Что такое валидаторы в Angular

Валидаторы в Angular представляют собой функции, которые получают значение контрола (поля формы) и возвращают либо null (если значение корректно), либо объект с ошибкой (если не прошло проверку). Эти функции могут применяться на уровне отдельных полей или групп форм и могут работать как синхронно, так и асинхронно (например, для проверки данных на сервере).

Angular предоставляет набор встроенных валидаторов, позволяя добавить их к форме при создании, используя FormBuilder или напрямую через FormControl. Однако когда выходит за рамки стандартных проврок, приходится реализовывать свои решения, которые должны вписываться в архитектуру Angular Forms.

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

Создание синхронного кастомного валидатора

Синхронный кастомный валидатор — это обычная функция TypeScript, прнимающая AbstractControl и возвращающая либо null (если данных ошибок нет), либо объект с ошибкой. Такая функция может быть реализована прямо внутри модуля или вынесена в отдельный файл для переиспользования.

Ниже представлен пример простого валидатора, который проверяет, что значение поля не содержит запрещённое слово:

export function forbiddenWordValidator(forbiddenWord: string): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const forbidden = control.value?.includes(forbiddenWord);
    return forbidden ? { 'forbiddenWord': { value: control.value } } : null;
  };
}

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

Объект ошибки, возвращаемый валидатором, может содержать дополнительные данные (например, запрещённое слово), чтобы упростить локализацию и вывод сообщений об ошибках на интерфейсе.

Подключение кастомных валидаторов к форме

Чтобы использовать кастомный валидатор, его необходимо добавить к FormControl или FormGroup, как при инициализации, так и динамически. Пример, как добавить вышеописанный валидатор через FormBuilder:

this.form = this.fb.group({
  username: ['', [Validators.required, forbiddenWordValidator('admin')]],
});

В случае необходимости можно добавить валидаторы позже с помощью метода setValidators:

const usernameControl = this.form.get('username');
usernameControl.setValidators([forbiddenWordValidator('admin')]);
usernameControl.updateValueAndValidity();

Angular сам обработает результат функции валидатора и отразит состояние контрола (invalid, valid, errors), обеспечив корректное отображение ошибок на UI. Для вывода сообщений обычно используют директиву ngIf:

Имя пользователя содержит запрещенное слово!

Таким образом кастомные валидаторы органично интегрируются в стандартный жизненный цикл формы Angular.

Асинхронные кастомные валидаторы

Иногда валидация невозможна без обращения к внешним API — например, проверка уникальности email в базе данных пользователя. Такой валидатор возвращает не объект, а Observable или Promise, с результатом проверки.

Пример асинхронного валидатора на уникальность email:

export function uniqueEmailValidator(userService: UserService): AsyncValidatorFn {
  return (control: AbstractControl): Observable => {
    return userService.isEmailTaken(control.value).pipe(
      map(isTaken => (isTaken ? { 'emailTaken': true } : null))
    );
  };
}

Такой валидатор подключается вторым параметром при создании FormControl:

this.form = this.fb.group({
  email: ['', [Validators.required, Validators.email], [uniqueEmailValidator(this.userService)]],
});

Асинхронные валидаторы особенно эффективны для сложных сценариев, однако требуют отдельной обработки состояния загрузки, задержек и отмены предыдущих запросов для предотвращения гонок.

Валидация на уровне группы (FormGroup)

Иногда необходимо валидировать значения сразу нескольких полей, например, убедиться, что пароли совпадают или дата окончания больше даты начала. Для этого применяют групповые валидаторы.

Пример валидатора, сравнивающего значения в двух полях:

export function passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
  const password = group.get('password')?.value;
  const confirm = group.get('confirmPassword')?.value;
  return password === confirm ? null : { 'passwordMismatch': true };
}

Его можно подключить к группе формы:

this.form = this.fb.group({
  password: ['', Validators.required],
  confirmPassword: ['', Validators.required]
}, { validator: passwordMatchValidator });

Вывод сообщения об ошибке аналогичен:

Пароли не совпадают

Использование групповых валидаторов позволяет реализовать любые комплексные проверки на бизнес-уровне.

Типичные примеры и лучшие практики

Кастомные валидаторы делают форму гибкой и мощной. Наиболее популярные сценарии использования:

  • Проверка «черного списка» имен, email, телефонов
  • Валидация числовых диапазонов с учетом других значений формы
  • Проверка уникальности значений через API
  • Проверка корректности паролей (длина, сложность, символы)
  • Валидация дат, сравнение дат начала и окончания

Важно придерживаться следующих рекомендаций:

  • Возвращайте максимально подробную информацию об ошибках для UI
  • Используйте композицию валидаторов для сложных кейсов
  • Старайтесь выносить валидаторы в отдельные файлы-функции для переиспользования
  • Не нагружайте асинхронную валидацию частыми запросами (используйте debounceTime на уровне формы)

Это позволит сделать код чище, а клиентский опыт — комфортнее.

Таблица: сравнение типов валидаторов

Тип валидатора Когда использовать Форма возвращаемого значения Особенности
Синхронный Проверки, не требующие асинхронных действий или доступа к API ValidationErrors | null Высокая производительность, простотой интеграции
Асинхронный Проверки, требующие обращения к серверу или работе с большими объемами данных Observable<ValidationErrors | null> либо Promise Необходима организация отмены запросов, работа с загрузкой и ошибками сети
Групповой Сравнение значений между разными контролами ValidationErrors | null Подключается к FormGroup, позволяет реализовать зависимые проверки

Заключение

Создание кастомных валидаторов — мощная возможность Angular Forms, которая позволяет адаптировать валидацию под любые требования бизнеса. В этой статье мы рассмотрели синхронные, асинхронные и групповые валидаторы, их подключение к формам, и типичные сценарии использования. Грамотное проектирование и вынесение логики проверки в отдельные функции делает код масштабируемым и поддерживаемым. При разработке пользовательских валидаторов важно следовать принципу инкапсуляции логики и уделять внимание удобству использования для UI.

Освоив кастомные валидаторы в Angular, вы сможете создавать сложные динамические формы, соответствующие самым строгим требованиям к качеству данных и безопасности.

Запрос 1 Запрос 2 Запрос 3 Запрос 4 Запрос 5
кастомные валидаторы Angular создание валидаторов в Angular Forms проверка формы Angular своими функциями настройка валидаторов в Angular Reactive Forms примеры кастомных валидаторов Angular
Запрос 6 Запрос 7 Запрос 8 Запрос 9 Запрос 10
валидация форм с Angular Custom Validators создать пользовательский валидатор Angular Angular формы валидаторы примеры как сделать валидатор в Angular Forms валидаторы для форм Angular Reactive