Angular

Валидация форм

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

  • Регистрация: проверка уникальности email запросом к серверу до отправки формы
  • Смена пароля: совпадение нового пароля и подтверждения через валидатор группы
  • Бронирование: дата выезда не раньше даты заезда, кросс-полевая проверка
  • Промокоды: асинхронная проверка существования кода с индикатором PENDING
  • Платёжные формы: формат карты через pattern плюс кастомный алгоритм Луна

Предварительные знания

  • Реактивные формы: FormControl, FormGroup, как читается статус и errors
  • Базовые RxJS-операторы для асинхронных валидаторов: map, debounceTime, switchMap
  • Понимание чистых функций и их сигнатур в TypeScript

Валидатор как функция

С самого появления реактивных форм в Angular 2 валидатор задан как чистая функция: она принимает AbstractControl и возвращает либо объект ошибок ValidationErrors, либо null, если значение валидно. Эта простая контрактная модель не изменилась за годы. Встроенные валидаторы в классе Validators (required, email, min, pattern и другие) - это те же функции. Асинхронные валидаторы добавили для серверных проверок: они возвращают Observable или Promise того же объекта ошибок. В Signal Forms (стабильны с v22) идея сохранилась, но обёрнута в декларативную schema.

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

Встроенные валидаторы собраны в классе Validators: required, requiredTrue, min, max, minLength, maxLength, email, pattern. Они навешиваются массивом при создании контрола. Кастомный валидатор - это функция той же сигнатуры: принимает AbstractControl, возвращает объект ошибки или null.

Функция-фабрика forbiddenWords принимает параметр и возвращает ValidatorFn, замыкая список запрещённых слов. Если совпадение найдено, возвращается объект с ключом forbiddenWord и деталями ошибки; иначе null. Ключ становится свойством control.errors.

Объект ошибки - это карта ключ-значение. Ключ (например, required или forbiddenWord) идентифицирует тип ошибки, а значение несёт детали для сообщения. Несколько валидаторов могут вернуть несколько ключей одновременно.

Что должен вернуть валидатор, если значение контрола корректно?

Кросс-полевая валидация

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

Валидатор группы передаётся вторым аргументом fb.group в опциях. Ошибка passwordsMismatch попадает в errors самой группы, а не отдельного контрола. Поэтому в шаблоне её читают через form.errors, а не через ошибки поля confirm.

Если ошибку группы удобнее показывать у конкретного поля, кросс-полевой валидатор может вызвать confirm.setErrors. Тогда ошибка окажется в errors контрола confirm и отобразится рядом с ним привычным образом.

Почему валидатор совпадения паролей навешивают на FormGroup, а не на контрол?

Асинхронные валидаторы и показ ошибок

Асинхронный валидатор нужен, когда проверка требует обращения к серверу - например, уникальность email. Он возвращает Observable или Promise того же объекта ошибок. Пока ответ не пришёл, контрол находится в статусе PENDING, и форма не считается валидной.

Асинхронные валидаторы передаются третьим аргументом контрола, отдельно от синхронных. Angular запускает их только после того, как синхронные валидаторы прошли, чтобы не дёргать сервер при заведомо невалидном значении.

Опция updateOn: 'blur' критична для асинхронных валидаторов: без неё запрос полетит на каждое нажатие клавиши. Запуск проверки при потере фокуса убирает шквал запросов и снижает нагрузку на сервер.

В каком статусе находится контрол, пока асинхронный валидатор ждёт ответа сервера?

Связь с другими темами

Валидация - надстройка над реактивной моделью, тесно связанная с HTTP:

  • Reactive forms — Базовая модель, на контролы и группы которой навешиваются валидаторы
  • Функциональные интерсепторы — Асинхронные валидаторы шлют HTTP-запросы, а серверные ошибки нормализует interceptor

Итог

  • Валидатор - чистая функция (control) => ValidationErrors | null, встроенные собраны в классе Validators
  • Кастомный валидатор возвращает объект с ключом-ошибкой, который попадает в control.errors
  • Асинхронный валидатор возвращает Observable или Promise ошибок, контрол при этом в статусе PENDING
  • Кросс-полевая валидация навешивается на FormGroup, потому что видит сразу несколько контролов
  • Ошибки показываются по control.errors с учётом touched и dirty, чтобы не пугать пользователя раньше времени

Связанные уроки

  • ng-30-reactive-forms — Валидаторы навешиваются на FormControl и FormGroup, поэтому реактивную модель нужно знать заранее
  • ng-28-interceptors — Асинхронные валидаторы и серверные ошибки идут по HTTP, который проходит через interceptor
Валидация форм

0

1

Войти