1. Template-driven Forms
Форма — это не просто набор <input> и <button>. Это способ структурировать ввод пользователя, валидировать данные, отображать ошибки, собирать информацию для отправки на сервер и многое другое. В современных приложениях формы встречаются повсюду: регистрация, авторизация, фильтры, поиск, настройки профиля, обратная связь — и этот список можно продолжать до бесконечности.
Angular предоставляет два основных способа работы с формами:
- Template-driven forms — формы, управляемые через шаблон (HTML).
- Reactive forms — формы, управляемые через TypeScript-код (модель).
Давайте разберёмся, чем они отличаются, и какой подход лучше выбрать в разных ситуациях.
Краткое описание
Template-driven формы (или "шаблонные формы") — это подход, при котором почти вся логика формы (валидация, сбор данных, обработка ошибок) описывается прямо в HTML-шаблоне компонента. TypeScript-код компонента обычно минимален. Angular сам под капотом связывает шаблон с моделью.
Главная идея:
Вы описываете структуру формы в HTML, а Angular автоматически создаёт "форменную модель" по вашим атрибутам.
Когда использовать Template-driven формы?
- Простые формы (2–10 полей), без сложной вложенности.
- Быстрые прототипы, формы с минимальной валидацией.
- Когда хочется писать минимум кода на TypeScript.
Как это выглядит?
Шаг 1: Импорт FormsModule
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
FormsModule,
// ... другие модули
]
})
export class AppModule { }
Шаг 2: Шаблон формы
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<label>
Имя:
<input name="name" ngModel required>
</label>
<label>
Email:
<input name="email" ngModel email>
</label>
<button type="submit">Отправить</button>
</form>
Шаг 3: Компонент
export class UserFormComponent {
onSubmit(form: any) {
console.log(form.value); // { name: '...', email: '...' }
}
}
Как это работает?
- Атрибут ngModel связывает поле ввода с моделью Angular.
- Angular сам создаёт объект формы, который можно получить через #userForm="ngForm".
- Валидация (например, required, email) указывается прямо в HTML.
Пример двухсторонней привязки
<input [(ngModel)]="userName" name="userName">
Всё просто и похоже на магию! Но магия работает только для несложных случаев.
2. Reactive Forms — формы, управляемые моделью
Reactive Forms (или "реактивные формы", или "модельные формы") — это подход, при котором вся логика формы (структура, валидация, значения, ошибки) описывается в TypeScript-коде компонента. Шаблон становится максимально "глупым" — он только отображает поля и ошибки.
Главная идея:
Вы явно создаёте модель формы в коде, полностью контролируете её состояние и реакцию на изменения.
Когда использовать Reactive Forms?
- Сложные формы (много полей, вложенность, динамические поля).
- Требуется сложная кастомная валидация (например, асинхронная проверка на сервере).
- Нужно отслеживать изменения в форме (реагировать на каждое нажатие).
- Когда форма может меняться динамически (например, добавление/удаление полей).
Как это выглядит?
Шаг 1: Импорт ReactiveFormsModule
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
ReactiveFormsModule,
// ... другие модули
]
})
export class AppModule { }
Шаг 2: Компонент
import { FormGroup, FormControl, Validators } from '@angular/forms';
export class UserFormComponent {
userForm = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email])
});
onSubmit() {
console.log(this.userForm.value); // { name: '...', email: '...' }
}
}
Шаг 3: Шаблон
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
<label>
Имя:
<input formControlName="name">
</label>
<label>
Email:
<input formControlName="email">
</label>
<button type="submit">Отправить</button>
</form>
Как это работает?
- Вы явно создаёте структуру формы через FormGroup и FormControl.
- Валидация задаётся в коде.
- В шаблоне используется директива [formGroup] и formControlName.
- Все данные, ошибки, состояние доступны через объект формы (userForm).
3. Полезные нюансы
Сравнение Template-driven и Reactive Forms
| Template-driven | Reactive Forms | |
|---|---|---|
| Где описывается? | В шаблоне (HTML) | В TypeScript-коде |
| Модуль | FormsModule | ReactiveFormsModule |
| Двусторонняя связь | |
Нет (односторонняя) |
| Сложность | Просто, "магия" | Явно, больше кода |
| Валидация | В шаблоне (атрибуты) | В коде () |
| Динамические поля | Сложно | Легко |
| Тестируемость | Сложнее | Легко |
| Работа с вложенными структурами | Ограничена | Удобно |
| Асинхронная валидация | Сложно | Просто |
Аналогия
Template-driven — вы пишете всё на HTML, а Angular под капотом волшебно связывает форму с моделью. Это как автоматическая коробка передач: удобно, пока не начались пробки и горки.
Reactive Forms — вы управляете каждым аспектом формы из кода. Это как механика: чуть сложнее, но полная свобода и контроль.
Когда какой подход выбирать?
- Template-driven — если форма простая (несколько полей), не требуется сложная логика, хочется меньше кода. Отлично для быстрых прототипов, обратной связи, простых фильтров.
- Reactive Forms — если форма сложная, с динамическими полями, вложенными группами, нужна сложная валидация, асинхронные проверки, интеграция с сервисами, тестирование. Это стандарт для всего серьёзного.
Совет:
В реальных проектах 90% сложных форм делают на Reactive Forms. Template-driven формы — это как блокнот: удобно для быстрых заметок, но не для написания романа.
4. Практика: Пример простой формы двумя способами
Задача: Форма регистрации с полями "Имя", "Email", "Пароль"
Template-driven вариант
Шаблон:
<form #regForm="ngForm" (ngSubmit)="onSubmit(regForm)">
<label>
Имя:
<input name="name" ngModel required>
</label>
<div *ngIf="regForm.submitted && regForm.controls.name?.invalid">
Имя обязательно!
</div>
<label>
Email:
<input name="email" ngModel required email>
</label>
<div *ngIf="regForm.submitted && regForm.controls.email?.invalid">
Введите корректный email!
</div>
<label>
Пароль:
<input name="password" ngModel required minlength="6" type="password">
</label>
<div *ngIf="regForm.submitted && regForm.controls.password?.invalid">
Пароль должен быть не менее 6 символов!
</div>
<button type="submit">Зарегистрироваться</button>
</form>
Компонент:
export class RegisterComponent {
onSubmit(form: any) {
if (form.valid) {
// Отправляем данные на сервер
console.log(form.value);
}
}
}
Reactive Forms вариант
Компонент:
import { FormGroup, FormControl, Validators } from '@angular/forms';
export class RegisterComponent {
regForm = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required, Validators.minLength(6)])
});
onSubmit() {
if (this.regForm.valid) {
// Отправляем данные на сервер
console.log(this.regForm.value);
}
}
}
Шаблон:
<form [formGroup]="regForm" (ngSubmit)="onSubmit()">
<label>
Имя:
<input formControlName="name">
</label>
<div *ngIf="regForm.get('name')?.invalid && regForm.get('name')?.touched">
Имя обязательно!
</div>
<label>
Email:
<input formControlName="email">
</label>
<div *ngIf="regForm.get('email')?.invalid && regForm.get('email')?.touched">
Введите корректный email!
</div>
<label>
Пароль:
<input formControlName="password" type="password">
</label>
<div *ngIf="regForm.get('password')?.invalid && regForm.get('password')?.touched">
Пароль должен быть не менее 6 символов!
</div>
<button type="submit">Зарегистрироваться</button>
</form>
5. Типичные ошибки при работе с формами в Angular
Ошибка №1: Смешивание Template-driven и Reactive Forms в одном компоненте.
Angular не любит, когда вы пытаетесь использовать ngModel и formControlName одновременно. Это приводит к ошибкам и странному поведению. Решение: выберите один подход для компонента.
Ошибка №2: Отсутствие импорта нужного модуля.
Если вы забыли импортировать FormsModule или ReactiveFormsModule в модуль — ничего работать не будет, а Angular будет ругаться на неизвестные директивы.
Ошибка №3: Неуникальные имена полей в Template-driven формах.
Каждое поле с ngModel должно иметь уникальный атрибут name, иначе Angular не сможет правильно построить модель формы.
Ошибка №4: Ожидание двусторонней привязки в Reactive Forms.
В Reactive Forms нет [(ngModel)]! Не пытайтесь использовать его вместе с formControlName.
Ошибка №5: Не инициализированные значения в Reactive Forms.
Если вы не передали начальное значение в FormControl, оно будет null или '' — не забывайте про это при работе с формой.
Ошибка №6: Игнорирование состояния поля.
Валидацию и ошибки показывайте только если поле уже было в фокусе (touched) или пользователь попытался отправить форму. Не пугайте пользователя красным цветом с самого начала.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ