← Назад к вопросам

В чём разница между Reactive Forms и Template-driven формами?

2.0 Middle🔥 231 комментариев
#React#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Reactive Forms и Template-driven формами

Этот вопрос касается Angular. Это два подхода к работе с формами в Angular с разными уровнями контроля и сложности.

Template-driven формы (TDF)

Template-driven — это старый, простой подход, где форма управляется в HTML шаблоне с помощью директив.

<!-- template-driven-form.component.html -->
<form (ngSubmit)="onSubmit()" #myForm="ngForm">
  <!-- Двухсторонняя привязка с ngModel -->
  <input 
    type="email" 
    name="email" 
    [(ngModel)]="user.email" 
    required
    email
  />
  <span *ngIf="myForm.get('email')?.hasError('email')">Email не валиден</span>
  
  <input 
    type="password" 
    name="password" 
    [(ngModel)]="user.password" 
    required
    minlength="8"
  />
  
  <select name="country" [(ngModel)]="user.country" required>
    <option>Россия</option>
    <option>США</option>
  </select>
  
  <button type="submit" [disabled]="!myForm.valid">Отправить</button>
</form>
// template-driven-form.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-template-form',
  templateUrl: './template-driven-form.component.html',
})
export class TemplateDrivenFormComponent {
  user = {
    email: '',
    password: '',
    country: '',
  };

  onSubmit() {
    console.log('Form data:', this.user);
    // Отправляем на сервер
  }
}

Характеристики Template-driven:

  • Логика в HTML (директивы ngModel, ngForm)
  • Простые формы
  • Меньше кода
  • Меньше контроля
  • Асинхронная валидация сложнее

Reactive Forms (RF)

Reactive Forms — это современный, мощный подход, где форма управляется в TypeScript коде через FormBuilder/FormGroup.

// reactive-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-reactive-form',
  templateUrl: './reactive-form.component.html',
})
export class ReactiveFormComponent implements OnInit {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    // Создаём форму программно
    this.form = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(8)]],
      country: ['', Validators.required],
    });
  }

  ngOnInit() {
    // Подписываемся на изменения
    this.form.get('email')?.valueChanges.subscribe((value) => {
      console.log('Email изменён:', value);
    });
  }

  onSubmit() {
    if (this.form.valid) {
      console.log('Form data:', this.form.value);
      // Отправляем на сервер
    }
  }
}
<!-- reactive-form.component.html -->
<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <!-- Привязка к контролю без ngModel -->
  <input 
    type="email" 
    formControlName="email" 
    placeholder="Email"
  />
  <!-- Проверка ошибок через контроль -->
  <span *ngIf="form.get('email')?.hasError('email')">Email не валиден</span>
  
  <input 
    type="password" 
    formControlName="password"
    placeholder="Пароль"
  />
  <span *ngIf="form.get('password')?.hasError('minlength')">
    Минимум 8 символов
  </span>
  
  <select formControlName="country">
    <option value="">Выберите страну</option>
    <option value="ru">Россия</option>
    <option value="us">США</option>
  </select>
  
  <button type="submit" [disabled]="!form.valid">Отправить</button>
</form>

Сравнение подходов

АспектTemplate-drivenReactive
ЛогикаВ HTMLВ TypeScript
СложностьПростыеСложные
КодМеньшеБольше, но понятнее
КонтрольНизкийВысокий
ТестированиеСложнееЛегче
Асинхронная валидацияСложноПросто
Динамические поляСложноПросто
ПроизводительностьМедленнееБыстрее
RxJSНе требуетТребует знаний

Практические примеры

1. Асинхронная валидация (проверка email в БД):

// Reactive Forms — просто
import { AbstractControl, AsyncValidator, ValidationErrors } from '@angular/forms';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class EmailValidator implements AsyncValidator {
  constructor(private http: HttpClient) {}

  validate(control: AbstractControl) {
    return this.http.get(`/api/check-email/${control.value}`).pipe(
      map((result: any) => (result.exists ? { emailTaken: true } : null))
    );
  }
}

// Использование в форме
this.form = this.fb.group({
  email: [
    '',
    [Validators.required, Validators.email],
    [this.emailValidator], // Асинхронная валидация
  ],
});

2. Динамические поля (добавление/удаление):

// Reactive Forms — встроенная поддержка
this.form = this.fb.group({
  items: this.fb.array([
    this.fb.group({ name: '', price: '' }),
  ]),
});

get items() {
  return this.form.get('items') as FormArray;
}

addItem() {
  this.items.push(
    this.fb.group({ name: '', price: '' })
  );
}

removeItem(index: number) {
  this.items.removeAt(index);
}
<div formArrayName="items">
  <div *ngFor="let item of items.controls; let i = index" [formGroupName]="i">
    <input formControlName="name" placeholder="Название" />
    <input formControlName="price" placeholder="Цена" />
    <button (click)="removeItem(i)">Удалить</button>
  </div>
  <button (click)="addItem()">Добавить товар</button>
</div>

3. Реактивные обновления:

// Reactive Forms — RxJS операторы
this.form.get('email')?.valueChanges
  .pipe(
    debounceTime(300),  // Подождать 300мс
    distinctUntilChanged(),  // Только если значение изменилось
    switchMap((email) => this.http.get(`/api/check-email/${email}`))
  )
  .subscribe((result) => {
    console.log('Email статус:', result);
  });

// Комбинирование нескольких полей
combineLatest([
  this.form.get('password')?.valueChanges,
  this.form.get('confirmPassword')?.valueChanges,
])
  .pipe(
    map(([pass, confirm]) => pass === confirm),
  )
  .subscribe((match) => {
    if (!match) {
      this.form.setErrors({ passwordMismatch: true });
    }
  });

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

Template-driven (TDF):

  • Очень простые формы (логин, поиск)
  • Быстрый прототип
  • Немного полей
  • Новичок в Angular
<form #form="ngForm" (ngSubmit)="login(form.value)">
  <input type="email" name="email" [(ngModel)]="email" />
  <input type="password" name="password" [(ngModel)]="password" />
  <button [disabled]="form.invalid">Вход</button>
</form>

Reactive Forms (RF):

  • Сложные формы (регистрация, профиль, таблица)
  • Асинхронная валидация
  • Динамические поля
  • Опытный разработчик
  • Нужны unit-тесты
this.form = this.fb.group({
  email: [
    '',
    [Validators.required, Validators.email],
    [this.emailValidator],
  ],
  password: ['', [Validators.required, Validators.minLength(8)]],
  confirmPassword: [''],
}, { validators: passwordMatchValidator });

Миграция с TDF на Reactive

// Было (TDF)
export class LoginComponent {
  email = '';
  password = '';
}

// Стало (RF)
export class LoginComponent {
  form: FormGroup;

  constructor(fb: FormBuilder) {
    this.form = fb.group({
      email: ['', Validators.required],
      password: ['', Validators.required],
    });
  }
}

Резюме

Template-driven формы — простой подход с логикой в HTML, подходит для простых форм. Reactive Forms — мощный подход с логикой в TypeScript, подходит для сложных форм с асинхронной валидацией и динамическими полями. Современная практика Angular рекомендует использовать Reactive Forms, так как они обеспечивают лучший контроль, тестируемость и масштабируемость.