Модуль 21

Форми в Angular: Template-driven Forms

Ось готовий урок, згенерований за твоїм майстер-промптом.


🎓 Урок: Форми в Angular (Template-driven Forms)

Привіт, друзі! 👋 Це CS50... тобто, це наш курс з Angular!

Сьогодні ми розберемо тему, без якої будь-який веб-додаток залишається просто красивою картинкою. Ми поговоримо про Template-driven Forms.


1. 🔥 Вступ: Проблема та мотивація

Уявіть, що ви прийшли до банку, щоб відкрити рахунок. Але замість того, щоб дати вам бланк і ручку, клерк просто мовчки дивиться на вас. Ви намагаєтеся щось сказати, а він вас не чує. Дивно, правда?

Веб-сайт без форм — це саме такий "глухий" клерк. Ви бачите інформацію, але не можете нічого передати назад.

  • Як користувач має ввести свій логін?
  • Як клієнт замовить піцу з подвійним сиром?
  • Як ви відправите коментар під цим уроком?

Без форм інтернет був би просто набором цифрових газет. Нам потрібен діалог. Нам потрібно, щоб користувач міг ввести дані, а ми — їх схопити, перевірити та відправити на сервер.

В Angular є два шляхи це зробити. Сьогодні ми йдемо шляхом найменшого опору — Template-driven Forms.

Аналогія: Уявіть, що HTML-шаблон — це ваш бланк замовлення в ресторані. Ви (розробник) просто розмічаєте на папері: "Тут напиши страву", "Тут постав галочку, якщо з собою". Ви не наймаєте окремого менеджера (TypeScript-код), щоб стежити за кожною літерою, яку пише клієнт. Бланк (Template) сам керує процесом. Angular просто дивиться на цей бланк і робить магію.


2. 🧠 Теоретична база (Що "під капотом")

Давайте розберемося, як це працює, не засинаючи над документацією.

У Template-driven підході головним босом є HTML. Майже вся логіка (валідація, прив’язка даних) пишеться прямо в тегах.

🔑 Три кити, на яких це тримається:

  1. FormsModule: Це як вмикач електрики для форм. Якщо ви не підключите цей модуль у app.module.ts (або у imports вашого standalone компонента), Angular просто ігноруватиме ваші спроби створити форму.
  2. ngForm: Як тільки Angular бачить тег <form>, він автоматично (тихо і непомітно) створює об'єкт ngForm. Це "душа" вашої форми. Вона знає все: чи валідна форма, чи користувач щось змінював, і які там дані.
  3. [(ngModel)]: Знаменитий "банан у коробці" 🍌📦. Це двосторонній зв'язок.
    • Змінили змінну в коді -> змінилося значення в інпуті.
    • Написали щось в інпуті -> змінилася змінна в коді.

⚙️ Як це працює під капотом?

Коли ви додаєте директиву ngModel до звичайного <input>, Angular каже: "Ага! Ти хочеш, щоб я стежив за цим полем". Він створює екземпляр FormControl (контролер) для цього конкретного поля і прив'язує його до вашої змінної. Він слухає кожне натискання клавіші і миттєво оновлює дані.

Що треба запам'ятати залізно:

У Template-driven формах, якщо ви використовуєте ngModel у тегу <form>, ви обов'язково маєте додати атрибут name="...". Без імені Angular не знатиме, як назвати цю властивість у загальному об'єкті форми.


3. 🧪 Приклади (від простого до реального)

Приклад №1: Найпростіший інпут

У нас є змінна userName = 'David'. Ми хочемо її редагувати.

Що ви очікуєте побачити в коді? Просто HTML і змінну.

// component.ts
export class UserComponent {
  userName: string = 'David';
}
<!-- component.html -->
<label>Ім'я:</label>
<!-- Банан у коробці -->
<input [(ngModel)]="userName" type="text">

<p>Привіт, {{ userName }}!</p>

Що відбувається: Ви друкуєте в полі, і текст у параграфі <p> змінюється миттєво. Магія синхронізації.


Приклад №2: Повноцінна форма логіну

А тепер реальне життя. Нам треба логін і пароль. І нам треба "спіймати" момент відправки форми.

Питання до вас: як ми отримаємо доступ до всієї форми, щоб перевірити, чи вона валідна?

Відповідь: Використовуємо Template Reference Variable (змінну шаблону) — #loginForm.

// login.component.ts
export class LoginComponent {
  user = {
    email: '',
    password: ''
  };

  onSubmit(form: any) {
    console.log('Дані форми:', form.value);
    console.log('Чи валідна форма?', form.valid);
  }
}
<!-- login.component.html -->
<!-- Створюємо посилання #myForm на директиву ngForm -->
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">

  <div>
    <label>Email:</label>
    <!-- name="email" ОБОВ'ЯЗКОВО! -->
    <input 
      type="email" 
      name="email" 
      [(ngModel)]="user.email" 
      required 
      email>
  </div>

  <div>
    <label>Password:</label>
    <input 
      type="password" 
      name="password" 
      [(ngModel)]="user.password" 
      required 
      minlength="6">
  </div>

  <!-- Кнопка блокується, якщо форма не валідна -->
  <button type="submit" [disabled]="!myForm.valid">Увійти</button>

</form>

Розбір польотів: 1. #myForm="ngForm": Ми дали ім'я всій формі, щоб звертатися до неї в HTML (наприклад, для кнопки disabled). 2. required, email, minlength: Це звичайні HTML5 атрибути, але Angular їх "розуміє" і використовує для валідації. 3. Якщо ви введете "abc" у поле email, myForm.valid стане false, і кнопка вимкнеться.


4. 🛠 Практична частина

Час забруднити руки кодом! Відкривайте IDE або StackBlitz.

Завдання 1: "Дзеркало" Створіть інпут і заголовок <h1>. Зробіть так, щоб текст, який ви вводите в інпут, одразу ставав заголовком сторінки.

Завдання 2: "Забудько" Візьміть приклад №2 (Логін). Видаліть атрибут name="email" з першого інпуту. Запустіть код. Відкрийте консоль браузера. Що ви бачите? (Спойлер: Angular буде сваритися червоним текстом. Прочитайте помилку і зрозумійте її).

Завдання 3: "Чекбокс долі" Додайте до форми чекбокс "Я погоджуюсь з правилами". Зробіть так, щоб кнопка "Відправити" була активною тільки тоді, коли чекбокс натиснутий (навіть якщо інші поля заповнені вірно).

Завдання 4: Міні-кейс "Список покупок" Створіть просту форму з одним полем "Продукт" і кнопкою "Додати". 1. Коли користувач тисне "Додати", продукт має падати в масив products і відображатися списком <ul> знизу. 2. Важливо: Після додавання поле вводу має очиститися!

Завдання 5: "А що, якщо..." Що буде, якщо використати [ngModel] (без дужок) замість [(ngModel)]? Спробуйте. Введіть значення в інпут. Чи зміниться змінна в компоненті? Чому? (Це називається одностороння прив'язка).


5. 💡 Мислення як у розробника

Коли ви працюєте з формами, легко потрапити в пастку. Ось як мислять досвідчені Angular-розробники:

  1. Помилка новачка: Забути FormsModule.

    • Симптом: Ви пишете [(ngModel)], а консоль кричить: "Can't bind to 'ngModel' since it isn't a known property of 'input'".
    • Рішення: Біжіть в модуль і імпортуйте його. Це класика, всі через це проходили.
  2. Про "Спагетті-код" в HTML:

    • Template-driven форми класні, бо прості. Але якщо ви бачите, що у вашому HTML куча умов *ngIf, складні перевірки валідації на 5 рядків — зупиніться.
    • Думка сеньйора: "Тут стає занадто складно для шаблону. Можливо, час переходити на Reactive Forms або винести логіку в TypeScript".
  3. Візуальний зворотний зв'язок (UX):

    • Користувачі ненавидять, коли форма червоніє ще до того, як вони почали писати.
    • Використовуйте властивості touched (користувач торкнувся поля) і dirty (користувач змінив значення).
    • Правило: Показуйте помилку "Email некоректний" тільки якщо email.invalid && email.touched. Не лякайте людей одразу!

6. 🧩 Підсумок

Отже, що ми маємо на виході?

  • Ви знаєте, як створити форму, не пишучи купу коду в класі.
  • Ви вмієте використовувати "банан у коробці" [(ngModel)] для зв'язку даних.
  • Ви зрозуміли, що <form> в Angular — це живий організм, який знає про свій стан (валідний він чи ні).
  • Ви пам'ятаєте про атрибут name, без якого магія не працює.

Ви тепер вмієте: Робити прості форми реєстрації, пошуку, зворотного зв'язку за 5 хвилин.

🤔 Тизер: Але уявіть ситуацію: вам треба зробити форму, де поля додаються динамічно (наприклад, кнопка "Додати ще одного пасажира"). Або вам треба перевіряти, чи такий email вже існує в базі даних поки користувач друкує. Робити це в шаблоні — пекло. Для таких задач "з зірочкою" Angular має потужну зброю — Reactive Forms. І саме про це ми поговоримо наступного разу.

А поки — кодьте і не забувайте закривати теги! Це був CS50! 👨‍💻🚀