Angular
Signal Forms
В реактивных формах модель и её данные живут раздельно: есть объект состояния и параллельное дерево FormControl, которые приходится синхронизировать. Signal Forms убирают этот дубль. Разработчик объявляет один writable-сигнал с данными, оборачивает его в form(), и форма становится прямым реактивным отражением модели. В Angular 22 (июнь 2026) этот API стал стабильным, после экспериментального статуса в 21 и задаёт направление для нового кода.
- Формы поверх сигнального store: модель и форма используют один источник истины
- Сложные мастера: производные поля через computed без ручной синхронизации
- Zoneless-приложения: формы на сигналах естественно вписываются в реактивность без зон
- Динамические схемы: правила валидации описываются декларативно в одной schema-функции
- Совместное состояние: один сигнал данных читается и формой, и остальным UI без копирования
Предварительные знания
- Реактивные формы: FormGroup, FormControl, валидаторы и их ограничения
- Сигналы: signal, computed, writable vs readonly, основы реактивности
- Строгий TypeScript: интерфейсы, дженерики, отсутствие any и явных кастов
Третья модель форм на сигналах
После того как сигналы стали основой реактивности Angular (v16-v19), команда начала проектировать формы прямо на них. Signal Forms прошли путь от экспериментального предложения до стабильного релиза в Angular 22 (июнь 2026). Ключевая идея: форма - это не отдельное дерево объектов, а реактивная обёртка над сигналом с данными. Состояние модели и состояние формы перестают расходиться. Реактивные формы при этом не исчезают: они остаются для существующего кода, а Signal Forms становятся рекомендованным выбором для нового.
Модель: сигнал данных и form()
Signal Form начинается с writable-сигнала, хранящего данные формы как обычный объект. Функция form() оборачивает этот сигнал и возвращает дерево полей, повторяющее структуру объекта. Источник истины один: меняется сигнал - меняется форма, и наоборот.
Тип ProfileModel задаёт форму данных, и form() выводит структуру полей из него. Никаких any и явных кастов: дерево полей типизировано на основе модели, поэтому обращение к несуществующему полю - ошибка компиляции, а не рантайма.
Директива Control связывает поле формы с элементом ввода в обе стороны. В отличие от formControlName, имя поля не задаётся строкой: профиль дерева полей берётся напрямую из типизированной модели, что исключает опечатки в именах.
Что именно принимает функция form() в Signal Forms?
Поля как сигналы состояния
Каждый узел дерева полей - функция-аксессор. Её вызов возвращает объект со state-сигналами поля: value, valid, errors, touched, dirty. Это значит, что состояние формы читается реактивно прямо в шаблоне через computed и слежение за сигналами, без подписок.
Вызов profileForm.name() даёт состояние поля name. Все его свойства - сигналы, поэтому computed автоматически пересчитывается при изменении value, touched или errors. Ошибка показывается только после touched, как и в других подходах, но без ручной подписки.
Поскольку состояние поля это сигналы, оно прозрачно встраивается в zoneless change detection. Шаблон перерисовывает только то, что зависит от изменившегося сигнала, без обхода всего дерева компонентов.
Что возвращает вызов аксессора поля, например profileForm.name()?
Schema валидации и отличие от реактивных форм
Валидация в Signal Forms описывается декларативно в schema-функции, которую form() принимает вторым аргументом. Внутри schema правила вроде required, email и validate навешиваются на конкретные поля через путь к ним. Отдельного массива валидаторов на каждом контроле, как в реактивных формах, больше нет.
Колбэк validate получает контекст с сигналом value и возвращает либо null (поле валидно), либо объект ошибки. Это типобезопасно: ctx.value() имеет тип number для поля age, поэтому сравнение с 18 проверяется компилятором без какого-либо приведения типов.
- Reactive forms — Модель данных и дерево FormControl отдельны и синхронизируются. Состояние - через Observable valueChanges. Валидаторы - массив на каждом контроле
- Signal Forms — Один сигнал данных как источник истины, дерево полей выводится из него. Состояние - через сигналы. Валидация - декларативная schema
Signal Forms и реактивные формы решают одну задачу разными средствами. Смешивать их в одной форме не нужно. Для нового кода в Angular 22 команда рекомендует Signal Forms, существующие реактивные формы переписывать ради переписывания не требуется.
Чем модель состояния Signal Forms принципиально отличается от реактивных форм?
Связь с другими темами
Signal Forms - третья и самая новая модель форм, выросшая из сигналов:
- Reactive forms — Предшественник: те же идеи валидации и структуры, но на Observable и отдельном дереве контролов
- Сигналы — Фундамент: form() оборачивает writable-сигнал, а производные поля - это computed
Итог
- Signal Forms стабильны в Angular 22 (экспериментальны в 21) и строят форму как реактивную обёртку над сигналом с данными
- Функция form() принимает writable-сигнал модели и возвращает дерево полей, отражающее её структуру
- Каждое поле - функция-аксессор: вызов field() даёт объект с сигналами value, valid, errors, touched
- Валидация описывается декларативно в schema через правила вроде required и validate, без отдельного массива валидаторов
- Главное отличие от реактивных форм: один источник истины-сигнал вместо параллельной синхронизации модели и FormControl
Связанные уроки
- ng-30-reactive-forms — Signal Forms наследуют идеи реактивных форм, поэтому модель FormGroup и валидаторы нужно знать заранее
- ng-11-signals-intro — Signal Forms построены на сигналах: signal, computed и реактивность - фундамент этого API