В Angular2 есть два подхода к построению форм. Один из них - формы на основе шаблонов. Другой способ - это реактивные формы. Когда мы создаем довольно сложные формы, мы можем захотеть написать пользовательские компоненты для пользовательских элементов формы, а затем скомпоновать несколько из этих компонентов в конкретную форму.

В этой статье мы покажем на примере, как реализовать вложенные формы с помощью API реактивных форм. В официальной документации Angular2 этот подход также называется «динамическими формами». Мы также попытаемся понять концепцию модели формы, которая поддерживает конкретную форму и элементы ввода.

Чтобы прояснить ситуацию для читателя, рекомендуется сначала выделить различия между формами на основе шаблонов и реактивными формами. Во второй части мы создадим вложенную реактивную форму.

Понимание модели формы

В Angular2 приложение содержит модель формы, которая представляет входные значения, состояние проверки, набор элементов управления и так далее. Базовыми строительными блоками этого API являются FormControl, FormGroupи FormArray. Затем экземпляры этих объектов отображаются в DOM.

Давайте просто возьмем простую форму со следующими элементами управления:

  • имя: текст, обязательно
  • количество: число

Форма на основе шаблонов

Формы на основе шаблонов, вероятно, наиболее естественны для тех, кто впервые работает с формами Angular2. Он начинается с шаблона HTML, элемента <form> и двух <input>elements .

Две директивы воплощают эту форму в жизнь: ngForm и ngModel.

Сначала выражение #myForm="ngForm" создает экземпляр директивыngForm и присваивает его переменной шаблона myForm.

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

NgForm создает модель формы, читая шаблон HTML.

Форма отражается выражениемmyForm.form, экземпляром FormGroup.

Затем для каждого элемента с NgModeldirective Angular создает FormControl.

Если в элементе ввода установлен обязательный атрибут, он добавляет обязательный валидатор к элементу управления. Затем экземпляр FormControl добавляется к родительскому FormGroup, где к нему можно получить доступ по его имени. Таким образом, мы получаем следующую модель:

  • myForm.form: FormGroup, представляющий форму
  • myForm.form.name a FormControl, представляющий первый вход
  • myForm.form.quantity: FormControl, представляющий второй вход

Вообще говоря, с формами, управляемыми шаблонами, мы объявляем нашу модель формы, записывая простые старомодные HTML-формы и позволяя Angular создать модель формы из DOM.

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

Теперь, в реактивных формах, мы пишем эту модель формы в коде, а затем связываем экземпляры FormGroup и FormControl в DOM.

В этом шаблоне мы привязываем член myForm к форме элемента DOM, записывая [formGroup]="myForm".

Выражение создает экземпляр FormGroupDirective, который обрабатывает привязку.

Затем мы привязываем FormControl экземпляров к входным элементам в DOM. Пример: formControlName="quantity".

Угадай, что? Это выражение создает FormControlDirective за кулисами.

В коде компонента модель формы создается с помощью ngOnInit()method. Нам также необходимо создать экземпляр и добавить код Validatorsin.

Так называемые FormBuilderlets вы создаете эти экземпляры. Совершенно другой стиль написания - создание экземпляров с newkeyword. Оба стиля имеют одинаковый эффект - во времена внедрения зависимостей просто никто не привык писать newanymore, что, вероятно, является причиной для конструктора, который, в свою очередь, вводится как зависимость конструктора.

Вы можете опробовать обе формы здесь:



Мне остаться или идти?

В чем вся неразбериха о формах на основе шаблонов и реактивных формах? Стоит ли использовать первое? Или последнее?

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

Когда мы создаем вложенные формы, это важный момент. С помощью реактивных форм мы можем построить модель формы и передать эту модель (или ее части) от одного компонента к другому через Входные свойства. Мы сделаем это во второй части этой истории.

Создание вложенной реактивной формы

Мы опираемся на предыдущий пример и улучшаем форму, чтобы сделать ее более интерактивной.

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

Обратите внимание, что мы поместили красную рамку вокруг недопустимых частей формы.

Таким образом, даже если вы введете имя для второго элемента, элементы FormArray будут недействительными и будут отмечены красным, поскольку сумма количеств ниже порогового значения (100 + 100 ‹300).

Форма не будет действительна до тех пор, пока вы не введете 200 и 100 в количественные входы (или что-либо еще, что в сумме составляет 300 или более).

Покажи мне код!

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



Извлечение компонента ItemFormControlComponent

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

Чтобы это сработало, мы передаем его индекс и FormGroupinstance в качестве входных свойств. FormGroup создается простой статической buildItem() функцией.

Нам также необходимо, чтобы кнопка удаления оставалась активной, для чего мы добавляем свойство вывода. Когда элемент формы удаляется, он генерирует индекс элемента. В родительском компоненте мы соединяем все вещи, добавляя наш собственный <item-control>tag в шаблон.

Извлечение компонента ItemsFormArrayComponent

Второй шаг - извлечь массив элементов в собственный компонент.

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

Опять же, мы передаем FormArray в качестве входного свойства от родителя к вновь созданному компоненту. Обратите внимание на небольшое отличие от предыдущего подхода: теперь мы пишем formArrayName="items" на главном элементе компонента. Код такой:

<items-array formArrayName="items" [items]="myForm.get('items')">

Конечно, мы можем повторно использовать ItemFormControlComponent из предыдущего шага. Вот как это выглядит:

Дерево компонентов и директив

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

Компоненты выделены темно-красными рамками. Директивы показаны в белых квадратах.

Вторая строка текста иллюстрирует элемент DOM, который является целью монтирования определенного компонента или директивы.

Входные свойства написаны на краях, которые соединяют компоненты / директивы.

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

Продолжая отсюда

Давайте просто кратко подведем итоги того, что мы рассмотрели:

API модели форм построен на основе FormControl, _49 _, _ 50_. Директивы связывают существующую модель формы с шаблонами (реактивные формы) или создают модель формы из шаблона (формы на основе шаблонов).

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

Если вы хотите проверить полную демонстрационную версию приложения, взгляните на Plunker ниже. Отзывы тоже очень приветствуются!