JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Подключение ReactiveFormsM...

Подключение ReactiveFormsModule, базовый синтаксис

Модуль 4: Node.js, Next.js и Angular
16 уровень , 5 лекция
Открыта

1. Знакомство с Reactive Forms

Reactive Forms — это подход к созданию форм в Angular, при котором все данные, структура и логика формы описываются в TypeScript-коде, а не только в шаблоне. Если Template-driven формы можно сравнить с автоматической коробкой передач ("Angular сам за тебя всё рулит, а ты только указываешь, что куда привязать"), то Reactive Forms — это механика: ты сам управляешь каждым элементом, но получаешь полный контроль.

Когда стоит использовать Reactive Forms?

  • Когда форма большая, сложная или динамически меняется (например, появляются или исчезают поля).
  • Когда нужна сложная валидация, зависящая от других полей или внешних условий.
  • Когда хочется тестировать формы unit-тестами.
  • Когда нужно управлять состоянием формы из кода (например, сбрасывать/заполнять формы, динамически строить их структуру).

Забавный факт:
Reactive Forms построены по принципу реактивного программирования. Все изменения формы — это потоки событий, которые можно слушать, на них можно подписываться и обрабатывать их, как настоящие программисты будущего.

2. Как подключить ReactiveFormsModule

Перед тем как начать творить чудеса с реактивными формами, нужно подключить специальный модуль — ReactiveFormsModule. Без него Angular даже не поймёт, что вы хотите от жизни.

Шаг 1. Импорт модуля

Откройте файл модуля вашего приложения, обычно это app.module.ts, и добавьте импорт:

import { ReactiveFormsModule } from '@angular/forms';

Шаг 2. Добавление в массив imports

В том же файле найдите массив imports внутри декоратора @NgModule и добавьте туда ReactiveFormsModule:


@NgModule({
  declarations: [
    // ... ваши компоненты
  ],
  imports: [
    // ... другие модули
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Всё! Теперь вы готовы к созданию реактивных форм. Если забыть этот шаг, Angular будет жаловаться на непонятные директивы вроде formControlName и будет обижаться на отсутствие формы.

3. Базовые строительные блоки Reactive Forms

В реактивных формах всё крутится вокруг трёх классов:

  • FormControl — управляет состоянием и значением отдельного поля ввода.
  • FormGroup — объединяет несколько FormControl в логическую группу (например, вся форма).
  • FormArray — массив FormControl или FormGroup, для динамических списков (например, список телефонов).

Краткая аналогия

  • FormControl — как отдельный переключатель.
  • FormGroup — как панель с кучей переключателей.
  • FormArray — как ряд одинаковых переключателей, которые можно добавлять или удалять.

Для начала мы сосредоточимся на FormControl и FormGroup.

4. Первый пример: простая форма с одним полем

Давайте создадим форму с одним полем (например, имя пользователя) и научимся управлять этим полем из кода.

Импортируем нужные классы

В компоненте, где будет форма, импортируем классы:

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

Создаём FormControl


export class SimpleFormComponent {
  name = new FormControl(''); // начальное значение — пустая строка
}

Связываем FormControl с шаблоном


<form>
  <input type="text" [formControl]="name" placeholder="Введите имя">
</form>
<p>Вы ввели: {{ name.value }}</p>

Примечание:
Здесь мы используем квадратные скобки: [formControl]="name", потому что связываем свойство компонента и элемент формы.

Как это работает?

  • Когда пользователь вводит текст, значение автоматически обновляется в объекте name.
  • Когда вы меняете значение через код (this.name.setValue('Гермиона')), оно появляется в поле ввода.

5. Пример сложнее: форма с несколькими полями (FormGroup)

В реальных приложениях формы почти всегда содержат больше одного поля. Для этого используют FormGroup.

Импортируем FormGroup

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

Создаём FormGroup в компоненте


export class LoginFormComponent {
  loginForm = new FormGroup({
    email: new FormControl(''),
    password: new FormControl('')
  });
}

Связываем FormGroup с шаблоном


<form [formGroup]="loginForm">
  <label>
    Email:
    <input type="email" formControlName="email">
  </label>
  <br>
  <label>
    Пароль:
    <input type="password" formControlName="password">
  </label>
  <br>
  <button type="submit">Войти</button>
</form>

<p>Текущее значение формы: {{ loginForm.value | json }}</p>

Как это работает?

  • Директива [formGroup]="loginForm" говорит Angular, что вся форма управляется этим объектом.
  • Каждый formControlName="..." связывает поле ввода с соответствующим контролом в группе.
  • loginForm.value — это объект с текущими значениями всех полей.

6. Обработка событий отправки формы

Реактивная форма не отправляется "по-настоящему" (как в старых добрых HTML-формах), если не обработать событие отправки.

Обработка submit

В шаблоне:


<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
  <!-- ...поля... -->
  <button type="submit">Войти</button>
</form>

В компоненте:


onSubmit() {
  console.log('Данные формы:', this.loginForm.value);
}

Важно:
Событие ngSubmit сработает только если кнопка имеет type="submit", и форма находится внутри тега <form>. Не забывайте эти детали!

7. Валидация: встроенные валидаторы

Reactive Forms позволяют легко добавлять валидацию прямо в конструкторе формы.

Импортируем Validators

import { Validators } from '@angular/forms';

Добавляем валидаторы


loginForm = new FormGroup({
  email: new FormControl('', [Validators.required, Validators.email]),
  password: new FormControl('', [Validators.required, Validators.minLength(6)])
});
  • Validators.required — поле обязательно.
  • Validators.email — значение должно быть похоже на email.
  • Validators.minLength(6) — минимум 6 символов.

Проверка ошибок в шаблоне


<input type="email" formControlName="email">
<div *ngIf="loginForm.get('email')?.invalid && loginForm.get('email')?.touched">
  <small *ngIf="loginForm.get('email')?.errors?.['required']">Email обязателен</small>
  <small *ngIf="loginForm.get('email')?.errors?.['email']">Некорректный email</small>
</div>

8. Практический пример: форма регистрации

Давайте соберём всё вместе и создадим простую форму регистрации:

Компонент


import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-register-form',
  templateUrl: './register-form.component.html'
})
export class RegisterFormComponent {
  registerForm = new FormGroup({
    username: new FormControl('', [Validators.required]),
    email: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [Validators.required, Validators.minLength(8)])
  });

  onSubmit() {
    if (this.registerForm.valid) {
      console.log('Регистрация:', this.registerForm.value);
      // Здесь может быть отправка данных на сервер
    } else {
      console.log('Форма невалидна!');
    }
  }
}

Шаблон


<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
  <label>
    Имя пользователя:
    <input type="text" formControlName="username">
  </label>
  <div *ngIf="registerForm.get('username')?.invalid && registerForm.get('username')?.touched">
    <small>Имя обязательно</small>
  </div>

  <label>
    Email:
    <input type="email" formControlName="email">
  </label>
  <div *ngIf="registerForm.get('email')?.invalid && registerForm.get('email')?.touched">
    <small *ngIf="registerForm.get('email')?.errors?.['required']">Email обязателен</small>
    <small *ngIf="registerForm.get('email')?.errors?.['email']">Некорректный email</small>
  </div>

  <label>
    Пароль:
    <input type="password" formControlName="password">
  </label>
  <div *ngIf="registerForm.get('password')?.invalid && registerForm.get('password')?.touched">
    <small *ngIf="registerForm.get('password')?.errors?.['required']">Пароль обязателен</small>
    <small *ngIf="registerForm.get('password')?.errors?.['minlength']">Минимум 8 символов</small>
  </div>

  <button type="submit" [disabled]="registerForm.invalid">Зарегистрироваться</button>
</form>

9. Схема: как работает реактивная форма


[FormGroup] --- (username: FormControl)
            |
            +--- (email: FormControl)
            |
            +--- (password: FormControl)

Пояснение:

  • Все поля объединены в одну группу.
  • Значения и валидность формы доступны через объект FormGroup.
  • Любое изменение поля сразу отражается в состоянии всей формы.

10. Типичные ошибки при подключении и использовании Reactive Forms

Ошибка №1: забыли импортировать ReactiveFormsModule.
Angular не понимает директивы formGroup, formControlName, formControl и кидает ошибку вида "Can't bind to 'formGroup' since it isn't a known property...". Проверьте, что в вашем модуле есть импорт ReactiveFormsModule.

Ошибка №2: не используете FormGroup, а пытаетесь сразу писать formControlName.
formControlName работает только внутри формы, связанной с [formGroup]. Если забыли добавить [formGroup]="...", Angular не сможет найти контекст.

Ошибка №3: не инициализированы все контролы в FormGroup.
Если в шаблоне есть поле с formControlName="email", а в FormGroup забыли его добавить — будет ошибка "Cannot find control with name: 'email'".

Ошибка №4: неправильное использование formControl (без квадратных скобок).
Вместо [formControl]="name" случайно пишут formControl="name", и Angular воспринимает "name" как строку, а не как переменную.

Ошибка №5: забыли обработать отправку формы через (ngSubmit).
Если не добавить обработчик (ngSubmit), то форма не будет реагировать на Enter и submit, а просто перезагрузит страницу.

Ошибка №6: не обновляете значения формы программно.
В Reactive Forms значения нужно менять через методы .setValue() или .patchValue(), а не напрямую присваивать.

Ошибка №7: забыли отключить кнопку отправки при невалидной форме.
Пользователь может отправить пустую или некорректную форму, если не добавить [disabled]="registerForm.invalid" к кнопке.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ