Реактивные формы и валидация

Реактивная форма — это модель формы в коде: структура, значения и правила валидации описаны в классе, а не разбросаны по HTML.

«Шаблонные формы — это форма, нарисованная в HTML. Реактивные — форма, спроектированная в коде, где ей и место в серьёзном приложении».

Формы — кровь бизнес-приложений: регистрация, оформление заказа, фильтры. Angular даёт два подхода, и для сложных случаев предпочтительны реактивные формы: вся структура и правила живут в TypeScript, что делает их предсказуемыми и тестируемыми. Строится форма из FormControl (одно поле) и FormGroup (группа полей):

import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'app-signup',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="submit()">
      <input formControlName="email" placeholder="Email" />
      @if (form.controls.email.invalid && form.controls.email.touched) {
        <small>Введите корректный email</small>
      }
      <input formControlName="password" type="password" />
      <button [disabled]="form.invalid">Регистрация</button>
    </form>
  `,
})
export class SignupComponent {
  private fb = inject(FormBuilder);
  form = this.fb.group({
    email: ['', [Validators.required, Validators.email]],
    password: ['', [Validators.required, Validators.minLength(8)]],
  });

  submit() {
    if (this.form.valid) console.log(this.form.value);
  }
}

Директива [formGroup] связывает форму с разметкой, formControlName привязывает каждое поле. Валидаторы (required, email, minLength) задают правила, а форма сама отслеживает состояние: valid, invalid, touched, dirty.

Как работает под капотом

Каждый FormControl — это объект с текущим значением и статусом. При вводе он прогоняет значение через цепочку валидаторов; каждый возвращает либо null (всё хорошо), либо объект ошибки. Статус группы выводится из статусов полей: группа невалидна, если невалидно хоть одно поле. Флаг touched ставится, когда пользователь покинул поле, — это позволяет показывать ошибку не сразу, а после взаимодействия.

   ввод в поле email
        |
   FormControl прогоняет валидаторы:
     required -> null (ок)
     email    -> { email: true } (ошибка!)
        |
   статус поля = INVALID
        |
   FormGroup: есть невалидное поле -> вся форма INVALID
        |
   [disabled]="form.invalid" -> кнопка заблокирована

Запускаемая врезка: валидация формы на JS

Соберём мини-движок валидации. «Попробуй сам ▶».

// валидаторы: возвращают null (ок) или текст ошибки
const required = v => (v ? null : 'обязательно');
const email    = v => (/.+@.+\..+/.test(v) ? null : 'неверный email');
const minLen = n => v => (v.length >= n ? null : 'минимум ' + n);

function validate(value, validators) {
  return validators.map(fn => fn(value)).filter(Boolean); // только ошибки
}

console.log(validate('', [required, email]));
// ["обязательно", "неверный email"]
console.log(validate('[email protected]', [required, email]));
// []  (валидно)
console.log(validate('123', [minLen(8)]));
// ["минимум 8"]

Частые ошибки

  • Забыть импортировать ReactiveFormsModule — директивы формы не заработают.
  • Показывать ошибку всегда. Проверяйте touched, чтобы не пугать пользователя до ввода.
  • Несовпадение formControlName и ключа в группе. Имена должны быть идентичны.

Best practices

  • Для нетривиальных форм выбирайте реактивный подход — он тестируем и масштабируется.
  • Блокируйте кнопку отправки при form.invalid.
  • Показывайте ошибки по условию invalid && touched.

Итоги. Реактивные формы описывают структуру и правила в коде через FormGroup/FormControl и валидаторы, автоматически отслеживая статус. Это надёжный выбор для серьёзных форм. Дальше — общение с сервером через HTTP.

Закрепляем

Реактивные формы переносят структуру и правила формы из HTML в TypeScript-код, и в этом их сила. Форма собирается из FormControl (одно поле) и FormGroup (группа полей), а правила задаются валидаторами вроде required, email и minLength. Поскольку вся модель формы живёт в коде, её легко читать, переиспользовать и покрывать тестами — поэтому для нетривиальных форм реактивный подход предпочтительнее шаблонного.

Форма сама ведёт богатый учёт своего состояния, и грамотный вывод ошибок опирается на эти флаги. valid и invalid говорят о соответствии правилам; touched ставится, когда пользователь покинул поле; dirty — когда значение менялось. Показывайте сообщение об ошибке по условию invalid && touched, чтобы не пугать человека красным текстом ещё до того, как он начал вводить данные. И блокируйте кнопку отправки при form.invalid — это простой способ не дать отправить заведомо некорректные данные. Не забудьте импортировать ReactiveFormsModule, иначе директивы формы не оживут.

ЭлементНазначение
FormControlОдно поле формы
FormGroupГруппа полей
ValidatorsПравила проверки
valid / touchedСостояние для вывода ошибок
Проверьте себя
1. Чем отличаются реактивные формы от шаблонных?
AНичем
BВ реактивных структура и правила валидации описаны в TypeScript-коде, а не в HTML — это предсказуемее и тестируемее
CРеактивные работают без JavaScript
DШаблонные нельзя валидировать
2. Зачем показывать ошибку только при invalid && touched?
AТак быстрее
BЧтобы не показывать ошибку до того, как пользователь взаимодействовал с полем
Ctouched ускоряет валидацию
DЭто требование TypeScript