Модуль 6 · урок 1 з 12
Два підходи до форм
Форми — друга за частотою задача в ентерпрайзі після таблиць. В Angular їх два види, і вибір між ними — типове питання на співбесіді. Відповідь коротка, але за нею стоїть різниця в тому, де живе стан форми.
Template-driven: стан у шаблоні
<form #f="ngForm" (ngSubmit)="save(f.value)">
<input name="title" [(ngModel)]="task.title" required minlength="3">
<button [disabled]="f.invalid">Зберегти</button>
</form>
Angular сам створює контроли, читаючи розмітку. Правила валідації — атрибути.
У класі майже нічого немає. Швидко для форми з трьох полів, і рівно тому обмежено:
щойно потрібна умовна валідація чи динамічні поля, шаблон перетворюється на кашу
з @if.
Reactive: стан у класі
export class TaskForm {
protected readonly form = new FormGroup({
title: new FormControl('', { nonNullable: true, validators: [Validators.required] }),
hours: new FormControl(1, { nonNullable: true }),
});
save(): void {
if (this.form.invalid) return;
this.api.create(this.form.getRawValue());
}
}
<form [formGroup]="form" (ngSubmit)="save()">
<input formControlName="title">
<input formControlName="hours" type="number">
</form>
Тут форма — це обʼєкт у коді. Шаблон лише привʼязується до вже наявної структури. Звідси й усі переваги: структуру видно в одному місці, її можна типізувати, тестувати, змінювати програмно й підписуватись на зміни через RxJS.
Порівняння
| Template-driven | Reactive | |
|---|---|---|
| Де стан | У шаблоні | У класі |
| Типізація | Практично немає | Повна |
| Динамічні поля | Важко | Природно |
| Складна валідація | Незручно | Просто |
| Підписка на зміни | Через ngModelChange | valueChanges — повноцінний потік |
| Тестування | Потрібен шаблон | Форму можна створити й перевірити без DOM |
| Асинхронна валідація | Обмежено | Штатно |
У таблиці ліда прямо написано «strongly typed ReactiveFormsModule» — тож
вибору немає, і це правильно. Але знати про template-driven варто: на робочих проєктах
трапляються обидва, і питання «чим вони відрізняються» звучить майже завжди.
Головна перевага реактивних форм: форма — це потік
this.form.valueChanges.pipe(
debounceTime(400),
takeUntilDestroyed(),
).subscribe(value => this.autosave(value));
this.form.controls.status.valueChanges.pipe(
takeUntilDestroyed(),
).subscribe(status => this.applyRulesFor(status));
this.form.statusChanges.subscribe(s => …); // VALID | INVALID | PENDING | DISABLED
Уся робота з модуля 4 тут окупається: автозбереження, реакція на зміну одного поля, асинхронна перевірка — усе це звичайні потоки, до яких застосовуються ті самі оператори.
Що знадобиться на екрані з таблицею
Наприкінці модуля в тебе буде модалка створення й редагування запису — з валідацією, перевіркою унікальності коду проти мокованих даних і попередженням про незбережені зміни. Тобто таблиця з модуля 5 нарешті отримає кнопки, і застосунок стане повноцінним CRUD.