Реактивные формы (Reactive Forms) в Angular 2+

Maksym Zhytlov
6 min readMay 9, 2017

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

Использование реактивных форм предполагает создание модели формы в классе компонента, связывание (two way data binding) этой модели с DOM элементами в шаблоне компонента используя ряд доступных директив.

Достоинства модель-ориентированных форм :

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

Реактивное программирование в Angular 2

Главная идея состоит в том, что элементы форм и сами формы обеспечивают Observable-based API. Вы можете думать о источниках данных (observables) как о коллекции значений изменяющихся в течение времени.
Т.е. реактивные формы и их отдельные элементы могут рассматриваться как постоянный источник данных на изменение которых можно подписаться и обрабатывать используя определенный набор методов.

Form API

Реактивные формы в Angular представлены тремя разными элементами формы.

  • FormControl — элемент ввода, может быть связан с input, textarea, select и прочими html элементами форм.
  • FormGroup— состоит из FormControl и FormGroup/FormArray, каждый элемент содержит строковый идентификатор. Может быть целой формой или же частью формы (вложенной формой).
  • FormArray — похож на FormGroup, но действует как массив (вы можете использовать методы push, insert, или removeAt), используется для форм с динамическим набором элементов (например: список IP адресов, список контактных данных);

Каждый этот элемент наследует от AbstractControl. Если мы посмотрим на этот абстрактный класс, то увидим что все они получили следующие свойства.

FormControl и FormGroup

FormControl — это класс, который позволяет напрямую создавать отдельные элементы ввода и управлять ими. Конструктор new FormControl() принимает три необязательных параметра:

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

Пример создания простого элемента ввода:

FormGroup объединяет в себеFromContro\FormArray. По сути, Ваша форма это всегда FormGroup. Например:

Другая приятная вещь это то, что FormGroup могут быть вложенными, а так же иметь собственный валидатор для всей группы. Например, поле address - это вложенный набор элементов ввода.

Связывание форм шаблона с помощью formGroup, formGroupName и formControlName

В данный момент в нашем коде нет ничего что говорит о том, что модель формы имеет отношение к форме в шаблоне компонента. Нам нужно связать модель с нашей формой и мы можем это сделать, используя директиву formGroup, которая принимает ссылку на экземпляр FormGroup.

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

Теперь мы можем пойти дальше и использовать formGroup для связывания модели с шаблоном формы:

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

Так как адрес у нас тоже является форм-группой, мы можем связать группу элементов управления формой с DOM элементами, используя formGroupName. Для этого нам нужен окружающий тег, иначе мы не сможем использовать эту директиву. Поэтому, обернем поля ввода в тег fieldset, к которому и применим эту директиву.

Теперь модель FormGroup соответствуют структуре DOM элементов.

FormArray и formArrayName

В нашем случае мы хотим, чтобы пользователь имел возможность добавлять неограниченное количество контактных данных. Именно для этих целей отлично подходит FormArray. Итак, объявим поле contacts как FormArray:

Чтобы иметь возможность динамически добавлять новые контакты мы в классе компонента определим метод addContact:

В коде приведенном выше мы через метод формы get получаем доступ к полю contacts и далее, как в обычный массив, добавляем в него новый объект FormGroup, который состоит из двух полей type и value.

Так же нам нужно дать пользователю возможность удалять контакты из массива. Для этого определим метод removeContact:

Здесь, как и выше, обращаемся к полю contacts и удаляем контакт, используя метод removeAt объекта FormArray, он принимает в качестве параметра индекс элемента, который нужно удалить.

Связывание FormArray с шаблоном формы:

Для этого я воспользовался директивой formArrayName, которая ссылается на наш объект FormArray, затем проитерировал все элементы массива с помощью директивы ngFor и связал вложенные элементы формы используя директиву [formControl].

Валидация реактивных форм

Теперь, когда модель формы связана с шаблоном, мы можем приступить к добавлению валидаторов для элементов управления формой. Чтобы использовать стандартные валидаторы нам нужно импортировать класс Validators из @angular/forms в нашем компоненте и передать их в качестве второго параметра конструкторам FormControl. Если нужно указать несколько валидаторов - они заключаются в массив. Пример:

К сожалению, в документации не отображен список всех стандартных валидаторов, но мы можем посмотреть его в исходном коде.

Добавление кастомных валидаторов

Кроме использования стандартных валидаторов, мы так же можем определять свои валидаторы. Например, создадим в классе компонента валидатор для поля user_name.

Валидатор для поля contacts:

Так же для валидации телефона:

И для проверки на совпадение паролей:

Теперь мы можем добавить их к нашей модели формы:

Вывод сообщений об ошибках валидации

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

В этом компоненте мы обращаемся к свойствам invalid и touched , чтобы узнать что пользователь уже ввел значение и являются ли они не корректными. А так же к полю errors объекта FormControl для того, чтобы узнать какие именно ошибки содержит поле. Теперь мы можем использовать validator-message компонент в шаблоне формы, например:

Динамичная валидация

Как уже упоминалось выше, в нашем случае каждый контакт — это форм-группа из двух элементов: type и value Элемент value должен валидироваться по разному в зависимости от выбранного type (e-mail, phone, skype).

В коде приведенном выше мы дописали в метод добавления нового контакта следующее:

  • подписались на изменение поля type в только что добавленной форм-группе с помощью valueChanges.subscribe;
  • установили новые валидоры используя метод setValidators;
  • вызвали метод updateValueAndValidity который пересчитывает значение и статус элемента;

Упрощение с помощью FormBuilder

В данный момент создание модели формы выглядит следующим образом:

Однако, данный код выглядит достаточно многословным, из-за многократного вызова new FormControl() и new FormGroup(). Но мы можем использовать API более высокого уровня, чтобы сделать определение формы немного лаконичнее.

FormBuilder — это синтаксический сахар, который создает экземпляры FormGrouop, FormControl, и FormArray вместо нас.

Теперь создание нашей формы с помощью FormBuilder выглядит более кратко:

На этом все, надеюсь этот материал был полезен для Вас. Я не претендую на звание гуру, поэтому если у Вас есть замечания / дополнения буду рад их увидеть в комментариях. А в следующей статье я рассмотрю создание кастомных компонентов элементов форм.

--

--