Формы в Angular¶
30.09.2026
Обработка пользовательского ввода формами лежит в основе многих обычных приложений.
Через формы пользователь входит в систему, обновляет профиль, вводит конфиденциальные данные и выполняет другие задачи ввода.
Angular даёт два подхода к вводу через формы: реактивные и шаблонные.
Оба перехватывают события ввода из представления, проверяют значения, создают модель формы и модель данных и дают способ отслеживать изменения.
Если нужны новые сигнальные формы, смотрите краткое руководство по сигнальным формам.
Это руководство помогает выбрать тип формы под задачу. В нём собраны общие строительные блоки обоих подходов. Здесь же кратко разобраны ключевые различия и показано, как они проявляются при настройке, в потоке данных и в тестах.
Выбор подхода¶
Реактивные и шаблонные формы по-разному обрабатывают данные формы и управляют ими. У каждого подхода свои преимущества.
| Формы | Подробности |
|---|---|
| Реактивные формы | Дают прямой и явный доступ к объектной модели формы. По сравнению с шаблонными формами они надёжнее: лучше масштабируются, их проще переиспользовать и тестировать. Если формы — важная часть приложения или приложение уже строится на реактивных приёмах, берите реактивные формы. |
| Шаблонные формы | Опираются на директивы в шаблоне, которые создают объектную модель и работают с ней. Удобны, когда в приложение нужно добавить простую форму, например подписку на рассылку. Их легко подключить, но масштабируются они хуже реактивных. Если требования к форме совсем простые и логика целиком умещается в шаблоне, шаблонные формы подойдут. |
Ключевые различия¶
В таблице ниже — основные различия реактивных и шаблонных форм.
| Реактивные | Шаблонные | |
|---|---|---|
| Настройка модели формы | Явная, создаётся в классе компонента | Неявная, создаётся директивами |
| Модель данных | Структурированная и неизменяемая | Неструктурированная и изменяемая |
| Поток данных | Синхронный | Асинхронный |
| Проверка формы | Функции | Директивы |
Масштабируемость¶
Если формы занимают центральное место в приложении, масштабируемость особенно важна. Модель формы должна переиспользоваться между компонентами.
Реактивные формы масштабируются лучше шаблонных. Они дают прямой доступ к API формы и используют синхронный поток данных между представлением и моделью данных, поэтому крупные формы собирать проще. Для тестов нужно меньше подготовки, и чтобы проверить обновление формы и валидацию, не требуется глубоко разбираться в обнаружении изменений.
Шаблонные формы рассчитаны на простые сценарии, и переиспользовать их сложнее. Они скрывают API формы и используют асинхронный поток данных между представлением и моделью данных. Эта абстракция сказывается и на тестах. Тесты сильно зависят от ручного запуска обнаружения изменений и требуют больше подготовки.
Настройка модели формы¶
И реактивные, и шаблонные формы отслеживают изменения значений между полями, с которыми работает пользователь, и данными формы в модели компонента. Общие строительные блоки у них одни, а создавать экземпляры элементов управления и управлять ими приходится по-разному.
Общие базовые классы формы¶
И реактивные, и шаблонные формы строятся на следующих базовых классах.
| Базовые классы | Подробности |
|---|---|
FormControl | Отслеживает значение и статус проверки отдельного элемента управления. |
FormGroup | Отслеживает те же значения и статус для набора элементов управления. |
FormArray | Отслеживает те же значения и статус для массива элементов управления. |
ControlValueAccessor | Связывает экземпляры Angular FormControl со встроенными элементами DOM. |
Настройка в реактивных формах¶
В реактивных формах модель формы задаётся прямо в классе компонента. Директива [formControl] связывает явно созданный экземпляр FormControl с конкретным элементом формы в представлении через внутренний аксессор значения.
Следующий компонент реализует одно поле ввода на реактивных формах. В этом примере модель формы — экземпляр FormControl.
favorite-color.component.ts
1 2 3 4 5 6 7 8 9 10 11 | |
В реактивных формах источник истины — модель формы: через директиву [formControl] на элементе <input> она в любой момент отдаёт значение и статус элемента.
Настройка в шаблонных формах¶
В шаблонных формах модель формы неявная. Директива NgModel создаёт экземпляр FormControl для элемента формы и управляет им.
Тот же компонент с одним полем, но уже на шаблонных формах.
favorite-color.component.ts
1 2 3 4 5 6 7 8 9 10 11 | |
В шаблонной форме источник истины — шаблон. Директива NgModel сама ведёт экземпляр FormControl.
Поток данных в формах¶
Если в приложении есть форма, Angular держит представление в согласии с моделью компонента, а модель компонента — с представлением. Когда пользователь меняет значения и делает выбор в представлении, новые значения должны попасть в модель данных. И наоборот: если логика программы меняет модель данных, представление должно это отразить.
Реактивные и шаблонные формы по-разному проводят данные от пользователя и от программных изменений. На схемах ниже — оба направления потока для каждого типа формы на примере поля любимого цвета.
Поток данных в реактивных формах¶
В реактивных формах каждый элемент формы в представлении напрямую связан с моделью формы (экземпляром FormControl). Обновления из представления в модель и из модели в представление идут синхронно и не зависят от того, как отрисован интерфейс.
Схема «представление → модель» показывает, как данные идут, когда значение поля меняется в представлении:
- Пользователь вводит значение в поле, в этом примере любимый цвет Blue.
- Элемент ввода формы порождает событие «input» с последним значением.
ControlValueAccessor, который слушает события элемента, сразу передаёт новое значение экземпляруFormControl.- Экземпляр
FormControlотдаёт новое значение через observablevalueChanges. - Все подписчики
valueChangesполучают новое значение.
1 2 3 4 5 6 7 8 9 10 | |
Схема «модель → представление» показывает, как программное изменение модели доходит до представления.
- Вызывается метод
favoriteColorControl.setValue(), и он обновляет значениеFormControl. - Экземпляр
FormControlотдаёт новое значение через observablevalueChanges. - Все подписчики
valueChangesполучают новое значение. - Аксессор значения на элементе ввода записывает в элемент новое значение.
1 2 3 4 5 6 7 8 9 10 | |
Поток данных в шаблонных формах¶
В шаблонных формах каждый элемент формы связан с директивой, которая ведёт модель формы внутри себя.
Схема «представление → модель» показывает, как данные идут, когда значение поля меняется в представлении.
- Пользователь вводит в поле Blue.
- Элемент ввода порождает событие «input» со значением Blue.
- Аксессор значения на поле вызывает метод
setValue()у экземпляраFormControl. - Экземпляр
FormControlотдаёт новое значение через observablevalueChanges. - Все подписчики
valueChangesполучают новое значение. - Аксессор значения также вызывает метод
NgModel.viewToModelUpdate(), который порождает событиеngModelChange. - Поскольку в шаблоне компонента для свойства
favoriteColorзадана двусторонняя привязка, свойствоfavoriteColorв компоненте принимает значение из событияngModelChange\(_Blue_\).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Схема «модель → представление» показывает, как данные идут из модели в представление, когда favoriteColor меняется с Blue на Red:
- В компоненте обновляется значение
favoriteColor. - Начинается обнаружение изменений.
- Во время обнаружения изменений у экземпляра директивы
NgModelвызывается хук жизненного циклаngOnChanges, потому что изменилось значение одного из её входов. - Метод
ngOnChanges()ставит в очередь асинхронную задачу, которая записывает значение во внутренний экземплярFormControl. - Обнаружение изменений завершается.
- На следующем тике выполняется задача записи значения в экземпляр
FormControl. - Экземпляр
FormControlотдаёт последнее значение через observablevalueChanges. - Все подписчики
valueChangesполучают новое значение. - Аксессор значения записывает в поле формы в представлении последнее значение
favoriteColor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
NgModel запускает второе обнаружение изменений, чтобы избежать ошибки ExpressionChangedAfterItHasBeenChecked: изменение значения приходит из привязки входа.
Изменяемость модели данных¶
Способ отслеживания изменений влияет на эффективность приложения.
| Формы | Подробности |
|---|---|
| Реактивные формы | Держат модель данных чистой и отдают её как неизменяемую структуру. При каждом изменении экземпляр FormControl возвращает новую модель данных, а не правит существующую. Уникальные изменения модели можно отслеживать через observable элемента управления. Обнаружение изменений работает эффективнее: обновление нужно только при уникальных изменениях. Поскольку обновления идут по реактивным правилам, данные можно преобразовывать операторами observable. |
| Шаблонные формы | Опираются на изменяемость и двустороннюю привязку: модель данных в компоненте обновляется по мере изменений в шаблоне. При двусторонней привязке у модели нет уникальных изменений, которые можно отследить, поэтому обнаружение изменений хуже понимает, когда обновление действительно нужно. |
Разница видна на предыдущих примерах с полем любимого цвета.
- В реактивных формах экземпляр
FormControlпри обновлении значения элемента управления всегда возвращает новое значение - В шаблонных формах свойство любимого цвета всегда изменяется на новое значение
Проверка формы¶
Проверка — неотъемлемая часть работы с любым набором форм. Нужно ли проверить обязательные поля или спросить внешний API, занято ли имя пользователя, Angular даёт набор встроенных валидаторов и возможность писать свои.
| Формы | Подробности |
|---|---|
| Реактивные формы | Свои валидаторы задаются функциями, которые получают элемент управления для проверки |
| Шаблонные формы | Привязаны к директивам шаблона: свой валидатор оформляется директивой, которая оборачивает функцию проверки |
Подробнее в разделе Проверка формы.
Тестирование¶
В сложных приложениях тесты занимают большое место. Простая стратегия тестирования помогает убедиться, что формы работают правильно. Реактивные и шаблонные формы в разной степени зависят от отрисовки интерфейса, когда проверки строятся на изменениях элемента управления и поля. В примерах ниже — как тестировать реактивные и шаблонные формы.
Тестирование реактивных форм¶
У реактивных форм относительно прямой путь к тестам: доступ к модели формы и модели данных синхронный, и интерфейс для проверки рисовать не нужно. В таких тестах статус и данные читают и меняют через элемент управления, не входя в цикл обнаружения изменений.
Следующие тесты берут компоненты любимого цвета из предыдущих примеров и проверяют потоки «представление → модель» и «модель → представление» для реактивной формы.
Проверка потока данных из представления в модель¶
Первый пример проверяет поток из представления в модель так:
- Найти в представлении элемент ввода формы и создать для теста собственное событие «input».
- Записать в поле новое значение Red и отправить событие «input» на элемент ввода.
- Убедиться, что значение
favoriteColorControlкомпонента совпадает со значением из поля.
Favorite color test - view to model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |
Следующий пример проверяет поток из модели в представление так:
- Через
favoriteColorControl, экземплярFormControl, задать новое значение. - Найти в представлении элемент ввода формы.
- Убедиться, что новое значение элемента управления совпадает со значением в поле.
Favorite color test - model to view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |
Тестирование шаблонных форм¶
Тесты шаблонных форм требуют хорошо понимать процесс обнаружения изменений и то, как директивы отрабатывают на каждом цикле: элементы нужно запрашивать, проверять и менять в нужный момент.
Следующие тесты берут те же компоненты любимого цвета и проверяют потоки из представления в модель и из модели в представление для шаблонной формы.
Следующий тест проверяет поток данных из представления в модель.
Favorite color test - view to model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
Шаги теста «представление → модель»:
- Найти в представлении элемент ввода формы и создать для теста собственное событие «input».
- Записать в поле новое значение Red и отправить событие «input» на элемент ввода.
- Запустить обнаружение изменений через тестовую фикстуру.
- Убедиться, что значение свойства
favoriteColorкомпонента совпадает со значением из поля.
Следующий тест проверяет поток данных из модели в представление.
Favorite color test - model to view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
Шаги теста «модель → представление»:
- Через экземпляр компонента задать значение свойства
favoriteColor. - Запустить обнаружение изменений через тестовую фикстуру.
- Дождаться следующей отрисовки через
await fixture.whenStable(). - Найти в представлении элемент ввода формы.
- Убедиться, что значение поля совпадает со значением свойства
favoriteColorв экземпляре компонента.
Что дальше¶
Подробнее о реактивных формах:
Подробнее о шаблонных формах:
Источник: https://angular.dev/guide/forms