Внести вклад
Подробные руководства
Формы

Сравнение с другими подходами к формам

Angular предоставляет три подхода к созданию форм: Signal Forms, Reactive Forms и Template-driven Forms. У каждого свои паттерны управления состоянием, валидацией и потоком данных. Это руководство помогает понять различия и выбрать подходящий подход для проекта.

Краткое сравнение

Возможность Signal Forms Reactive Forms Template-driven Forms
Источник истины Пользовательская writable signal-модель FormControl/FormGroup Модель пользователя в компоненте
Типобезопасность Выводится из модели Явная с typed forms Минимальная
Валидация Схема с path-based валидаторами Список валидаторов, передаваемых Controls На основе директив
Управление состоянием На основе сигналов На основе Observable Управляется Angular
Настройка Signal + функция схемы Дерево FormControl NgModel в шаблоне
Лучше всего для Приложений на сигналах Сложных форм Простых форм
Кривая обучения Средняя Средняя–высокая Низкая
Статус Stable (v22+) Stable Stable

На примере: форма входа

Лучший способ понять различия — увидеть одну и ту же форму, реализованную всеми тремя подходами.

Понимание различий

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

Где живут данные формы

Самое фундаментальное различие — где каждый подход считает «источником истины» значения формы.

Signal Forms хранят данные в writable signal. Когда нужны текущие значения формы, вы вызываете сигнал:

const credentials = this.loginModel(); // { email: '...', password: '...' }

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

Reactive Forms хранят данные внутри экземпляров FormControl и FormGroup. Значения доступны через иерархию формы:

const credentials = this.loginForm.value; // { email: '...', password: '...' }

Это отделяет управление состоянием формы от модели данных компонента. Структура формы явная, но требует больше кода настройки.

Template-driven Forms хранят данные в свойствах компонента. Значения доступны напрямую:

const credentials = {email: this.email, password: this.password};

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

Как работает валидация

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

Signal Forms используют функцию схемы, где валидаторы привязываются к путям полей:

loginForm = form(this.loginModel, (fieldPath) => {
  required(fieldPath.email, {message: 'Email is required'});
  email(fieldPath.email, {message: 'Enter a valid email address'});
});

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

Reactive Forms прикрепляют валидаторы при создании controls:

loginForm = new FormGroup({
  email: new FormControl('', [Validators.required, Validators.email]),
});

Валидаторы привязаны к отдельным controls в структуре формы. Это распределяет валидацию по определению формы. Сообщения об ошибках обычно живут в шаблоне.

Template-driven Forms используют атрибуты директив в шаблоне:

<input [(ngModel)]="email" required email />

Правила валидации живут в шаблоне рядом с HTML. Это держит валидацию близко к UI, но распределяет логику между шаблоном и компонентом.

Типобезопасность и автодополнение

Интеграция с TypeScript существенно различается между подходами, влияя на то, насколько компилятор помогает избегать ошибок.

Signal Forms выводят типы из структуры модели:

const loginModel = signal({email: '', password: ''});
const loginForm = form(loginModel);
// TypeScript knows: loginForm.email exists and returns FieldState<string>

Форма данных определяется один раз в сигнале, и TypeScript автоматически знает, какие поля существуют и их типы. Обращение к loginForm.username (которого нет) даёт ошибку типа.

Reactive Forms требуют явных аннотаций типов с typed forms:

const loginForm = new FormGroup({
  email: new FormControl<string>(''),
  password: new FormControl<string>(''),
});
// TypeScript knows: loginForm.controls.email is FormControl<string>

Типы указываются для каждого control отдельно. TypeScript проверяет структуру формы, но информацию о типах нужно поддерживать отдельно от модели данных.

Template-driven Forms предлагают минимальную типобезопасность:

email = '';
password = '';
// TypeScript only knows these are strings, no form-level typing

TypeScript понимает свойства компонента, но не знает о структуре формы или валидации. Проверка на этапе компиляции для операций с формой теряется.

Выбор подхода

Используйте Signal Forms, если:

  • Вы создаёте новые приложения на сигналах (Angular v22+)
  • Нужна типобезопасность, выводимая из структуры модели
  • Вам близка валидация на основе схем
  • Команда знакома с сигналами

Используйте Reactive Forms, если:

  • Нужна production-ready стабильность
  • Вы создаёте сложные динамические формы
  • Предпочитаете паттерны на основе Observable
  • Нужен тонкий контроль над состоянием формы
  • Вы работаете с существующей кодовой базой reactive forms

Используйте Template-driven Forms, если:

  • Вы создаёте простые формы (вход, контакт, поиск)
  • Делаете быстрый прототип
  • Логика формы прямолинейна
  • Предпочитаете держать логику формы в шаблонах
  • Вы работаете с существующей кодовой базой template-driven forms

Следующие шаги

Чтобы узнать больше о каждом подходе: