1. Знакомство с FormsModule
Сегодня мы делаем еще один шаг: подключаем FormsModule и учимся работать с шаблонными формами.
FormsModule — это модуль из пакета @angular/forms, который добавляет в ваше приложение магию двусторонней привязки данных через директиву ngModel, а также кучу полезных инструментов для работы с формами.
Без FormsModule Angular не будет понимать, что вы хотите использовать [(ngModel)] или директиву ngForm в шаблоне. Это как пытаться испечь пирог без духовки: ингредиенты есть, а результата нет.
Как подключить FormsModule
Откройте файл вашего основного модуля — обычно это app.module.ts.
Добавьте импорт:
import { FormsModule } from '@angular/forms';
Теперь добавьте FormsModule в массив imports:
@NgModule({
declarations: [
AppComponent,
// ... другие ваши компоненты
],
imports: [
BrowserModule,
FormsModule, // ← Вот здесь магия!
// ... другие модули, если есть
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Вот и всё! Теперь Angular будет знать, что вы хотите работать с формами как профи.
2. Первый шаблон формы: базовый синтаксис
Давайте создадим простую форму для ввода имени пользователя и выведем это имя на экран.
(Классика жанра — Hello, Angular Forms!)
Шаг 1: Добавим поле в компонент
В файле компонента (например, app.component.ts):
export class AppComponent {
userName: string = '';
}
Шаг 2: Добавим форму в шаблон
В файле шаблона (например, app.component.html):
<form>
<label for="username">Имя пользователя:</label>
<input id="username" type="text" [(ngModel)]="userName" name="username">
</form>
<p>Привет, {{ userName }}!</p>
Пояснение:
- [(ngModel)]="userName" — двусторонняя привязка: значение поля и переменная компонента всегда синхронизированы.
- name="username" — обязательно! Без этого Angular не сможет правильно отслеживать состояние поля в форме.
Внимание: Если забыть атрибут name, Angular будет жаловаться в консоли и директива работать не будет.
Как это работает?
- Пользователь вводит имя в поле — значение сразу попадает в свойство userName в компоненте.
- Переменная userName меняется — поле ввода автоматически обновляется.
- Всё это происходит без единой строчки кода в обработчиках событий!
3. Двусторонняя привязка: [(ngModel)]
В чем магия?
[(ngModel)] — это директива, которая объединяет property binding ([value]="...") и event binding ((input)="...") в одну удобную конструкцию.
- [value]="userName" — подставляет значение из компонента в поле ввода.
- (input)="userName = $event.target.value" — обновляет переменную при каждом вводе.
А с [(ngModel)] это делается автоматически и прозрачно для вас.
Пример с несколькими полями
Допустим, у нас форма регистрации с именем и email:
export class AppComponent {
name = '';
email = '';
}
<form>
<input type="text" [(ngModel)]="name" name="name" placeholder="Имя">
<input type="email" [(ngModel)]="email" name="email" placeholder="Email">
</form>
<p>Ваша заявка: {{ name }} ({{ email }})</p>
4. Полезные нюансы
ngForm: что это и зачем?
Когда вы используете <form> с полями ввода, Angular автоматически создаёт для него директиву ngForm.
Это позволяет отслеживать состояние всей формы: валидна ли она, была ли изменена, отправлена и т.д.
Пример:
<form #myForm="ngForm">
<input type="text" [(ngModel)]="name" name="name">
<button [disabled]="!myForm.valid">Отправить</button>
</form>
Пояснение:
- #myForm="ngForm" — создаёт локальную переменную шаблона, ссылающуюся на объект формы.
- myForm.valid — true, если все поля формы валидны (например, обязательные поля заполнены).
Валидаторы в шаблонных формах
Angular уже из коробки поддерживает простые HTML-валидаторы: required, minlength, maxlength, email и др.
<form #f="ngForm">
<input type="text" name="username" [(ngModel)]="userName" required minlength="3">
<div *ngIf="f.submitted && f.controls.username?.invalid">
<small *ngIf="f.controls.username?.errors?.required">Имя обязательно!</small>
<small *ngIf="f.controls.username?.errors?.minlength">Минимум 3 символа!</small>
</div>
<button>Отправить</button>
</form>
Пояснение:
- Атрибуты типа required, minlength работают автоматически.
- Через объект формы можно узнать, какие ошибки есть у поля.
5. Практика: мини-приложение «Добавь задачу»
Давайте продолжим развивать наше учебное приложение — теперь добавим форму для ввода задач (todo).
Шаг 1: В компоненте
export class AppComponent {
todoText = '';
todos: string[] = [];
addTodo() {
if (this.todoText.trim()) {
this.todos.push(this.todoText.trim());
this.todoText = '';
}
}
}
Шаг 2: В шаблоне
<form (ngSubmit)="addTodo()">
<input type="text" [(ngModel)]="todoText" name="todoText" required placeholder="Новая задача">
<button type="submit">Добавить</button>
</form>
<ul>
<li *ngFor="let task of todos">{{ task }}</li>
</ul>
Пояснение:
- [(ngModel)]="todoText" — двусторонняя привязка для поля ввода.
- (ngSubmit)="addTodo()" — обработка отправки формы.
- После добавления задача появляется в списке, а поле очищается.
6. Типичные ошибки при работе с FormsModule
Ошибка №1: Не подключён FormsModule.
Если забыть добавить FormsModule в массив imports модуля, Angular не узнает о директивах ngModel и ngForm. В консоли появится ошибка типа:
NG0303: Can't bind to 'ngModel' since it isn't a known property of 'input'.
Решение: подключите FormsModule в вашем AppModule!
Ошибка №2: Отсутствует атрибут name у поля.
Для каждого поля формы с [(ngModel)] обязательно указывать уникальный атрибут name. Без него Angular не сможет корректно отслеживать состояние поля в форме и покажет ошибку.
Ошибка №3: Попытка использовать [(ngModel)] с формами без FormsModule.
Если вы используете только ReactiveFormsModule, а FormsModule не подключён, директива [(ngModel)] работать не будет.
Ошибка №4: Дублирование имён полей.
Если несколько полей формы имеют одинаковое значение атрибута name, Angular будет путаться в состоянии этих полей. Следите, чтобы имя поля было уникальным в пределах формы.
Ошибка №5: Неочевидное поведение с валидацией.
Если вы используете валидацию через атрибуты типа required, но не проверяете состояние поля или формы (например, через form.valid), кнопка отправки может быть активна даже при невалидных данных.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ