Angular предоставляет два разных подхода к обработке пользовательского ввода через формы: реактивный и управляемый шаблонами.

Выбор подхода

Реактивные формы обеспечивают прямой, явный доступ к объектной модели базовой формы. По сравнению с формами на основе шаблонов они более надежны: их легче масштабировать, использовать повторно и тестировать. Если формы являются ключевой частью вашего приложения или вы уже используете реактивные шаблоны для создания своего приложения, используйте реактивные формы.

Формы, управляемые шаблоном, полагаются на директивы в шаблоне для создания базовой объектной модели и управления ею. Они полезны для добавления в приложение простой формы, например формы подписки на список адресов электронной почты. Их легко добавить в приложение, но они хуже масштабируются, чем реактивные формы. Если у вас есть очень простые требования к форме и логика, которой можно управлять исключительно в шаблоне, формы на основе шаблона могут подойти.

Ключевые отличия

Масштабируемость

Реактивные формы более масштабируемы, чем формы, управляемые шаблонами. Они обеспечивают прямой доступ к базовому API формы и используют синхронный поток данных между представлением и моделью данных, что упрощает создание крупномасштабных форм. Реактивные формы требуют меньше настроек для тестирования, а тестирование не требует глубокого понимания обнаружения изменений для надлежащего тестирования обновлений и проверки форм.

Формы на основе шаблонов ориентированы на простые сценарии и не так многоразовые. Они абстрагируются от базового API формы и используют асинхронный поток данных между представлением и моделью данных. Абстракция форм, управляемых шаблонами, также влияет на тестирование. Тесты в значительной степени зависят от ручного обнаружения изменений для правильной работы и требуют дополнительной настройки.

Настройка модели формы

Как реактивные, так и управляемые шаблонами формы построены на следующих базовых классах.

Базовые классы общей формы

FormControl:: Отслеживает значение и статус проверки отдельного элемента управления формы. FormGroup:: Отслеживает одни и те же значения и статус для коллекции элементов управления формы. FormArray:: Отслеживает одинаковые значения и статус для массива элементов управления формы. ControlValueAccessor:: Создает мост между экземплярами Angular FormControl и встроенными элементами DOM.

Настройка в реактивных формах

С реактивными формами вы определяете модель формы непосредственно в классе компонента. Директива [formControl] связывает явно созданный экземпляр FormControl с определенным элементом формы в представлении, используя метод доступа к внутреннему значению.

Следующий компонент реализует поле ввода для одного элемента управления с использованием реактивных форм. В этом примере модель формы является экземпляром FormControl.

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

@Component({
  selector: 'app-reactive-favorite-color',
  template: `
    Favorite Color: <input type="text" [formControl]="favoriteColorControl">
  `
})
export class FavoriteColorComponent {
  favoriteColorControl = new FormControl('');
}

Настройка в формах на основе шаблонов

В формах, управляемых шаблонами, модель формы является неявной, а не явной. Директива NgModel создает и управляет экземпляром FormControl для данного элемента формы.

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

@Component({
  selector: 'app-template-favorite-color',
  template: `
    Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
  `
})
export class FavoriteColorComponent {
  favoriteColor = '';
}

В управляемой шаблоном форме источником истины является шаблон. У вас нет прямого программного доступа к экземпляру FormControl.

Изменчивость модели данных

Реактивные формы: сохраняйте чистоту модели данных, предоставляя ее в виде неизменной структуры данных. Каждый раз, когда в модели данных инициируется изменение, экземпляр FormControl возвращает новую модель данных, а не обновляет существующую модель данных. Это дает вам возможность отслеживать уникальные изменения в модели данных с помощью наблюдаемых элементов управления. Обнаружение изменений более эффективно, поскольку требуется обновлять только уникальные изменения. Поскольку обновления данных следуют реактивным шаблонам, вы можете интегрировать наблюдаемые операторы для преобразования данных.

Формы на основе шаблонов: полагайтесь на изменчивость с двусторонней привязкой данных для обновления модели данных в компоненте по мере внесения изменений в шаблон. Поскольку при использовании двусторонней привязки данных в модели данных нет уникальных изменений, которые нужно отслеживать, обнаружение изменений менее эффективно при определении необходимости обновлений.

Реактивные формы

Существует три шага к использованию элементов управления формы. Зарегистрируйте модуль реактивных форм. Создайте новый FormControl. Зарегистрируйте элемент управления в шаблоне. Отобразите компонент.

Группировка элементов формы

Формы обычно содержат несколько связанных элементов управления. Реактивные формы предоставляют два способа группировки нескольких связанных элементов управления в одну форму ввода.

Группа форм: определяет форму с фиксированным набором элементов управления, которыми вы можете управлять вместе. Вы также можете вкладывать группы форм для создания более сложных форм.

Массив форм: определяет динамическую форму, в которую можно добавлять и удалять элементы управления во время выполнения. Вы также можете вкладывать массивы форм для создания более сложных форм.

Чтобы добавить группу форм к этому компоненту. Создайте экземпляр FormGroup. Свяжите модель FormGroup и представление.

При обновлении значения экземпляра группы форм, содержащего несколько элементов управления, может потребоваться обновить только части модели.

Обновление частей модели данных

setValue(): установите новое значение для отдельного элемента управления. Метод setValue() строго придерживается структуры группы форм и заменяет все значение для элемента управления.

patchValue(): замените любые свойства, определенные в объекте, которые были изменены в модели формы.

Строгие проверки метода setValue() помогают отловить ошибки вложенности в сложных формах, в то время как patchValue() автоматически завершает работу с этими ошибками.

Использование службы FormBuilder для создания элементов управления

Чтобы воспользоваться этой услугой, выполните следующие действия. Импортируйте класс FormBuilder. Внедрите эту зависимость, добавив ее в конструктор компонента. Сгенерируйте содержимое формы.

Создание динамических форм

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

Определить динамическую форму. Импортируйте класс FormArray. Определите элемент управления FormArray. Получите доступ к элементу управления FormArray с помощью метода получения. Отображение массива формы в шаблоне.

Типизированные формы

Начиная с Angular 14, реактивные формы строго типизированы по умолчанию.

С реактивными формами Angular вы явно указываете модель формы. В качестве простого примера рассмотрим эту базовую форму входа пользователя:

const login = new FormGroup({
    email: new FormControl(''),
    password: new FormControl(''),
});

В предыдущих версиях Angular большинство этих API-интерфейсов включали any где-то в свои типы, и взаимодействие со структурой элементов управления или самими значениями не было безопасным для типов. Например: вы можете написать следующий неверный код:

const emailDomain = login.value.email.domain;

Со строго типизированными реактивными формами приведенный выше код не компилируется, потому что в электронной почте нет свойства домена.

ФормКонтроль

const email = new FormControl('[email protected]');

Будет автоматически определено, что этот элемент управления имеет тип FormControl‹string|null›. Можно указать тип вместо того, чтобы полагаться на вывод. Рассмотрим элемент управления, который инициализирован нулем. Поскольку начальное значение равно null, TypeScript выведет FormControl‹null›, который уже, чем мы хотим.

const email = new FormControl(null);
email.setValue('[email protected]'); // Error!

Чтобы предотвратить это, мы явно указываем тип как string|null:

const email = new FormControl<string|null>(null);
email.setValue('[email protected]');

Массив форм

FormArray содержит открытый список элементов управления. Параметр type соответствует типу каждого внутреннего элемента управления:

const names = new FormArray([new FormControl('Alex')]);
names.push(new FormControl('Jess'));

Этот FormArray будет иметь тип внутренних элементов управления FormControl‹string|null›. Если вы хотите иметь несколько разных типов элементов внутри массива, вы должны использовать UntypedFormArray, потому что TypeScript не может сделать вывод, какой тип элемента будет встречаться в какой позиции.

FormGroup и FormRecord

В любой FormGroup можно отключить элементы управления. Любой отключенный элемент управления не будет отображаться в значении группы. Как следствие, тип login.value — Partial‹{email: string, password: string}›. Partial в этом типе означает, что каждый член может быть неопределенным. В частности, тип login.value.email — string|undefined. Некоторые виды использования FormGroup не соответствуют описанному выше шаблону, поскольку ключи заранее неизвестны. Класс FormRecord предназначен для этого случая.

FormBuilder и NonNullableFormBuilder

Этот тип является сокращением для указания {nonNullable: true} в каждом элементе управления и может исключить значительный шаблон из больших форм, не допускающих значение NULL.

const fb = new FormBuilder();
const login = fb.nonNullable.group({
    email: '',
    password: '',
});

В приведенном выше примере оба внутренних элемента управления не будут обнуляемыми (т. е. будет установлено значение nonNullable).