Реактивные формы задают подход от модели: так обрабатывают поля, значения которых меняются со временем. В этом руководстве — как создать и обновить базовый элемент управления, собрать несколько элементов в группу, проверить значения и сделать динамическую форму, в которую элементы добавляют и удаляют во время работы.
Реактивные формы ведут состояние формы в конкретный момент явно и неизменяемо. Каждое изменение состояния возвращает новое состояние, и между изменениями модель остаётся цельной. Реактивные формы строятся вокруг потоков observable: поля и значения формы доступны как потоки, которые можно читать синхронно.
Тестировать такие формы проще: в момент запроса данные согласованы и предсказуемы. Любой потребитель этих потоков может безопасно работать с данными.
Реактивные формы отличаются от шаблонных форм в нескольких существенных пунктах. Они дают синхронный доступ к модели данных, неизменяемость вместе с операторами observable и отслеживание изменений через потоки observable.
Шаблонные формы позволяют менять данные прямо из шаблона, но устроены менее явно: они опираются на директивы в шаблоне и на изменяемые данные, а изменения отслеживают асинхронно. Подробное сравнение двух подходов — в обзоре форм.
Чтобы пользоваться элементами управления, нужны три шага.
Сгенерировать новый компонент и подключить модуль реактивных форм. Этот модуль объявляет директивы, без которых реактивные формы не работают.
Создать экземпляр FormControl.
Зарегистрировать FormControl в шаблоне.
После этого форму показывают, добавив компонент в шаблон.
В примерах ниже — как добавить один элемент управления. Пользователь вводит имя в поле, значение перехватывается, и на экран выводится текущее значение элемента.
Генерация компонента и импорт ReactiveFormsModule¶
Командой CLI ng generate component создайте компонент в проекте, импортируйте ReactiveFormsModule из пакета @angular/forms и добавьте его в массив imports компонента.
Конструктор FormControl задаёт начальное значение — здесь это пустая строка. Если создавать элементы управления в классе компонента, состояние поля можно сразу слушать, обновлять и проверять.
После создания элемента управления в классе компонента его нужно связать с элементом формы в шаблоне. Обновите шаблон привязкой formControl, которую даёт FormControlDirective. Эта директива тоже входит в ReactiveFormsModule.
Синтаксис привязки в шаблоне регистрирует элемент управления на поле name. Элемент управления и элемент DOM общаются друг с другом: представление отражает изменения модели, а модель — изменения представления.
Показанное значение меняется по мере обновления элемента управления.
Реактивные формы отдают сведения об элементе управления через свойства и методы каждого экземпляра. Эти свойства и методы базового класса AbstractControl управляют состоянием формы и решают, когда показывать сообщения при проверке ввода.
У реактивных форм есть методы, которые меняют значение элемента управления из кода. Значение можно обновить без участия пользователя. У экземпляра есть метод setValue(): он записывает новое значение и проверяет, что структура переданного значения совпадает со структурой элемента управления. Например, когда данные формы приходят из бэкенд-API или сервиса, setValue() обновляет элемент управления новым значением и полностью заменяет старое.
В примере ниже в класс компонента добавляется метод, который через setValue() ставит значение Nancy.
Источник истины для элемента управления — модель формы. По нажатию кнопки значение поля меняется в классе компонента и перекрывает текущее.
В этом примере элемент управления один. Если вызывать setValue() у группы формы или массива формы, значение должно совпадать со структурой группы или массива.
Обычно в форме несколько связанных элементов управления. Реактивные формы дают два способа собрать их в одну форму.
Группы формы
Подробности
Группа формы
Задаёт форму с фиксированным набором элементов, которыми управляют вместе. Основы группы разобраны в этом разделе. Группы можно вкладывать, чтобы собирать более сложные формы.
Массив формы
Задаёт динамическую форму: элементы можно добавлять и удалять во время работы. Массивы тоже можно вкладывать. Подробнее в разделе Создание динамических форм.
Экземпляр элемента управления ведёт одно поле, а экземпляр группы — состояние целой группы элементов \(например, формы\). Каждый элемент в группе отслеживается по имени, заданному при создании группы. В примере ниже несколько экземпляров собраны в одну группу.
Сгенерируйте компонент ProfileEditor и импортируйте классы FormGroup и FormControl из пакета @angular/forms.
В классе компонента заведите свойство profileForm и запишите в него новый экземпляр группы. В конструктор группы передайте объект: именованные ключи сопоставлены своим элементам управления.
Для формы профиля добавьте два элемента с именами firstName и lastName.
Отдельные элементы управления теперь собраны в группу. Экземпляр FormGroup отдаёт значение модели объектом, собранным из значений своих элементов. У группы те же свойства (например, value и untouched) и методы (например, setValue()), что и у отдельного элемента управления.
Группа отслеживает статус и изменения каждого своего элемента: если меняется один, родитель тоже сообщает о новом статусе или значении. Модель группы держится на своих участниках. После описания модели обновите шаблон, чтобы модель отразилась в представлении.
profile-editor.component.html (template form group)
Как группа содержит набор элементов, так FormGroup с именем profileForm привязывается к элементу form директивой FormGroup и создаёт слой связи между моделью и формой с полями. Вход formControlName директивы FormControlName привязывает каждое поле к элементу управления, описанному в FormGroup. Элементы управления общаются со своими элементами DOM и передают изменения экземпляру группы — источнику истины для значения модели.
Компонент ProfileEditor принимает ввод пользователя, но в настоящем приложении значение формы нужно перехватить и отдать на обработку за пределы компонента. Директива FormGroup слушает событие submit элемента form и порождает событие ngSubmit, которое можно привязать к функции обратного вызова. Добавьте на тег form обработчик ngSubmit с методом onSubmit().
<!-- #docregion ng-submit --><form[formGroup]="profileForm"(ngSubmit)="onSubmit()"><!-- #enddocregion ng-submit --><labelfor="first-name">First Name: </label><inputid="first-name"type="text"formControlName="firstName"required/><labelfor="last-name">Last Name: </label><inputid="last-name"type="text"formControlName="lastName"/><divformGroupName="address"><h2>Address</h2><labelfor="street">Street: </label><inputid="street"type="text"formControlName="street"/><labelfor="city">City: </label><inputid="city"type="text"formControlName="city"/><labelfor="state">State: </label><inputid="state"type="text"formControlName="state"/><labelfor="zip">Zip Code: </label><inputid="zip"type="text"formControlName="zip"/></div><divformArrayName="aliases"><h2>Aliases</h2><buttontype="button"(click)="addAlias()">+ Add another alias</button>
@for (alias of aliases.controls; track $index; let i = $index) {
<div><!-- The repeated alias template --><labelfor="alias-{{ i }}">Alias:</label><inputid="alias-{{ i }}"type="text"[formControlName]="i"/></div>
}
</div><!-- #docregion submit-button --><p>Complete the form to enable button.</p><buttontype="submit"[disabled]="!profileForm.valid">Submit</button><!-- #enddocregion submit-button --></form><hr/><p>Form Value: {{ profileForm.value | json }}</p><!-- #docregion display-status --><p>Form Status: {{ profileForm.status }}</p><!-- #enddocregion display-status --><buttontype="button"(click)="updateProfile()">Update Profile</button>
Метод onSubmit() в компоненте ProfileEditor забирает текущее значение profileForm. Чтобы форма оставалась инкапсулированной, а значение уходило наружу, используйте output(). В примере ниже console.warn пишет сообщение в консоль браузера.
Событие submit порождает тег form встроенным событием DOM. Его вызывает кнопка с типом submit. Тогда пользователь может отправить заполненную форму клавишей Enter.
Кнопкой button добавьте в низ формы кнопку отправки.
<!-- #docregion ng-submit --><form[formGroup]="profileForm"(ngSubmit)="onSubmit()"><!-- #enddocregion ng-submit --><labelfor="first-name">First Name: </label><inputid="first-name"type="text"formControlName="firstName"required/><labelfor="last-name">Last Name: </label><inputid="last-name"type="text"formControlName="lastName"/><divformGroupName="address"><h2>Address</h2><labelfor="street">Street: </label><inputid="street"type="text"formControlName="street"/><labelfor="city">City: </label><inputid="city"type="text"formControlName="city"/><labelfor="state">State: </label><inputid="state"type="text"formControlName="state"/><labelfor="zip">Zip Code: </label><inputid="zip"type="text"formControlName="zip"/></div><divformArrayName="aliases"><h2>Aliases</h2><buttontype="button"(click)="addAlias()">+ Add another alias</button>
@for (alias of aliases.controls; track $index; let i = $index) {
<div><!-- The repeated alias template --><labelfor="alias-{{ i }}">Alias:</label><inputid="alias-{{ i }}"type="text"[formControlName]="i"/></div>
}
</div><!-- #docregion submit-button --><p>Complete the form to enable button.</p><buttontype="submit"[disabled]="!profileForm.valid">Submit</button><!-- #enddocregion submit-button --></form><hr/><p>Form Value: {{ profileForm.value | json }}</p><!-- #docregion display-status --><p>Form Status: {{ profileForm.status }}</p><!-- #enddocregion display-status --><buttontype="button"(click)="updateProfile()">Update Profile</button>
У кнопки в предыдущем фрагменте есть привязка disabled: кнопка отключается, когда profileForm невалидна. Проверки пока нет, поэтому кнопка всегда включена. Базовая проверка разобрана в разделе Проверка ввода формы.
В этом примере address group объединяет текущие элементы firstName и lastName с новыми street, city, state и zip. Элемент address в группе — ребёнок общего элемента profileForm, но правила значения и статуса те же. Изменения статуса и значения вложенной группы поднимаются к родительской и сохраняют согласие со всей моделью.
После обновления модели в классе компонента обновите шаблон: свяжите экземпляр группы с полями ввода. Добавьте в шаблон ProfileEditor группу address с полями street, city, state и zip.
profile-editor.component.html (template nested form group)
Когда у группы несколько элементов, часто нужно обновить только часть модели. В этом разделе — как обновлять отдельные части модели данных элемента управления.
Значение модели обновляют двумя способами:
Методы
Подробности
setValue()
Задаёт новое значение отдельному элементу. Метод setValue() строго следует структуре группы и заменяет значение элемента целиком.
patchValue()
Заменяет в модели формы те свойства из объекта, которые изменились.
Строгие проверки setValue() помогают поймать ошибки вложенности в сложных формах, а patchValue() при таких ошибках молча ничего не делает.
В ProfileEditorComponent метод updateProfile из примера ниже обновляет имя и улицу пользователя.
<form[formGroup]="profileForm"><labelfor="first-name">First Name: </label><inputid="first-name"type="text"formControlName="firstName"/><labelfor="last-name">Last Name: </label><inputid="last-name"type="text"formControlName="lastName"/><divformGroupName="address"><h2>Address</h2><labelfor="street">Street: </label><inputid="street"type="text"formControlName="street"/><labelfor="city">City: </label><inputid="city"type="text"formControlName="city"/><labelfor="state">State: </label><inputid="state"type="text"formControlName="state"/><labelfor="zip">Zip Code: </label><inputid="zip"type="text"formControlName="zip"/></div><divformArrayName="aliases"><h2>Aliases</h2><buttontype="button"(click)="addAlias()">+ Add another alias</button>
@for (alias of aliases.controls; track $index; let i = $index) {
<div><!-- The repeated alias template --><labelfor="alias-{{ i }}">Alias: </label><inputid="alias-{{ i }}"type="text"[formControlName]="i"/></div>
}
</div></form><p>Form Value: {{ profileForm.value | json }}</p><!-- #docregion patch-value --><buttontype="button"(click)="updateProfile()">Update Profile</button><!-- #enddocregion patch-value -->
По нажатию кнопки модель profileForm получает новые значения firstName и street. Обратите внимание: street лежит в объекте внутри свойства address. Так нужно, потому что patchValue() накладывает обновление на структуру модели. patchValue() меняет только те свойства, которые модель формы определяет.
Сервис FormBuilder для создания элементов управления¶
Создавать экземпляры вручную утомительно, когда форм много. Сервис FormBuilder даёт удобные методы для генерации элементов управления.
Чтобы воспользоваться сервисом, сделайте следующее.
Импортируйте класс FormBuilder.
Внедрите сервис FormBuilder.
Сгенерируйте содержимое формы.
В примерах ниже компонент ProfileEditor переписывается на сервис построителя формы: так создаются экземпляры элементов управления и групп.
У сервиса FormBuilder три метода: control(), group() и array(). Это фабрики: в классе компонента они создают элементы управления, группы и массивы формы. Метод group создаёт элементы profileForm.
В предыдущем примере метод group() получает тот же объект и описывает свойства модели. Значение каждого имени элемента — массив, и первый элемент массива задаёт начальное значение.
Элемент управления можно задать одним начальным значением. Если нужны синхронная или асинхронная проверка, добавьте синхронные и асинхронные валидаторы вторым и третьим элементами массива. Сравните построитель формы с ручным созданием экземпляров.
Проверка формы нужна, чтобы пользовательский ввод был полным и верным. В этом разделе к элементу управления добавляется один валидатор и выводится общий статус формы. Подробнее проверка разобрана в руководстве Проверка формы.
Чтобы добавить проверку, сделайте следующее.
Импортируйте функцию-валидатор в компонент формы.
Добавьте валидатор к полю формы.
Добавьте логику обработки статуса проверки.
Самая частая проверка — обязательное поле. В примере ниже к элементу firstName добавляется проверка на заполненность, и результат выводится на экран.
В реактивных формах есть набор функций-валидаторов для типичных случаев. Функция получает элемент управления и по результату проверки возвращает объект ошибки или null.
Импортируйте класс Validators из пакета @angular/forms.
Когда в элемент управления добавляют обязательное поле, начальный статус — невалидный. Этот статус поднимается к родительской группе, и её статус тоже становится невалидным. Текущий статус экземпляра группы читают через свойство status.
<!-- #docregion ng-submit --><form[formGroup]="profileForm"(ngSubmit)="onSubmit()"><!-- #enddocregion ng-submit --><labelfor="first-name">First Name: </label><inputid="first-name"type="text"formControlName="firstName"required/><labelfor="last-name">Last Name: </label><inputid="last-name"type="text"formControlName="lastName"/><divformGroupName="address"><h2>Address</h2><labelfor="street">Street: </label><inputid="street"type="text"formControlName="street"/><labelfor="city">City: </label><inputid="city"type="text"formControlName="city"/><labelfor="state">State: </label><inputid="state"type="text"formControlName="state"/><labelfor="zip">Zip Code: </label><inputid="zip"type="text"formControlName="zip"/></div><divformArrayName="aliases"><h2>Aliases</h2><buttontype="button"(click)="addAlias()">+ Add another alias</button>
@for (alias of aliases.controls; track $index; let i = $index) {
<div><!-- The repeated alias template --><labelfor="alias-{{ i }}">Alias:</label><inputid="alias-{{ i }}"type="text"[formControlName]="i"/></div>
}
</div><!-- #docregion submit-button --><p>Complete the form to enable button.</p><buttontype="submit"[disabled]="!profileForm.valid">Submit</button><!-- #enddocregion submit-button --></form><hr/><p>Form Value: {{ profileForm.value | json }}</p><!-- #docregion display-status --><p>Form Status: {{ profileForm.status }}</p><!-- #enddocregion display-status --><buttontype="button"(click)="updateProfile()">Update Profile</button>
Кнопка Submit отключена, потому что profileForm невалидна из-за обязательного элемента firstName. После заполнения поля firstName форма становится валидной, и кнопка Submit включается.
FormArray — альтернатива FormGroup, когда нужно вести любое число безымянных элементов управления. Как и у группы, у массива элементы можно вставлять и удалять динамически, а значение и статус проверки считаются по дочерним элементам. Ключ по имени для каждого элемента задавать не нужно, поэтому массив удобен, если число дочерних значений заранее неизвестно.
Чтобы описать динамическую форму, сделайте следующее.
Импортируйте класс FormArray.
Опишите элемент FormArray.
Получите доступ к элементу FormArray через геттер.
Выведите массив формы в шаблоне.
В примере ниже в ProfileEditor ведётся массив aliases.
Массив формы можно инициализировать любым числом элементов, от нуля и больше: их перечисляют в массиве. Добавьте в экземпляр группы profileForm свойство aliases и опишите им массив формы.
Метод FormBuilder.array() задаёт массив, а FormBuilder.control() заполняет его начальным элементом.
Геттер открывает доступ к aliases в экземпляре массива и избавляет от повторных вызовов profileForm.get(). Экземпляр массива — это неопределённое число элементов в массиве. Удобно доставать элемент через геттер, и тот же приём легко повторить для других элементов.
Синтаксисом геттера заведите свойство класса aliases: оно достаёт массив формы псевдонимов из родительской группы.
Возвращаемый элемент имеет тип AbstractControl, поэтому для методов экземпляра массива нужен явный тип. Опишите метод, который динамически вставляет элемент псевдонима в массив формы. Метод FormArray.push() добавляет элемент новым пунктом массива. В FormArray.push() можно передать и массив элементов, чтобы зарегистрировать несколько сразу.
Чтобы привязать aliases из модели формы, добавьте их в шаблон. По аналогии со входом formGroupName директивы FormGroupNameDirective, formArrayName связывает экземпляр массива с шаблоном через FormArrayNameDirective.
Добавьте следующий HTML шаблона после , закрывающего элемент formGroupName.
profile-editor.component.html (aliases form array template)
1 2 3 4 5 6 7 8 9101112
<divformArrayName="aliases"><h2>Aliases</h2><buttontype="button"(click)="addAlias()">+ Add another alias</button>
@for (alias of aliases.controls; track $index; let i = $index) {
<div><!-- The repeated alias template --><labelfor="alias-{{ i }}">Alias:</label><inputid="alias-{{ i }}"type="text"[formControlName]="i"/></div>
}
</div>
Блок @for обходит каждый экземпляр элемента управления из массива aliases. У элементов массива нет имён, поэтому индекс записывают в переменную i и передают каждому элементу, чтобы привязать его ко входу formControlName.
Каждый новый экземпляр псевдонима получает свой элемент по индексу. Так можно отслеживать каждый элемент, когда считаются статус и значение корневого элемента.
В приложениях без Zone.js (zoneless) изменение модели реактивных форм (например, вызов FormArray.push()) само по себе не планирует обнаружение изменений компонента. Если шаблон зависит от структурных изменений модели, таких как aliases.controls, компонент должен сообщить Angular, что нужно запустить обнаружение изменений. Например, свяжите observable формы с ChangeDetectorRef.markForCheck():
FormArrayDirective для массива формы верхнего уровня¶
FormArray можно привязать прямо к элементу <form> через FormArrayDirective. Это удобно, когда у формы нет FormGroup верхнего уровня и сам массив представляет всю модель формы.
Сначала в форме одно поле Alias. Чтобы добавить ещё, нажмите кнопку Add Alias. Массив псевдонимов можно сверить с моделью формы: её показывает Form Value внизу шаблона. Вместо отдельного элемента управления на каждый псевдоним можно собрать ещё одну группу с дополнительными полями. Элемент для каждого пункта описывается так же.
Все элементы управления отдают единый поток событий изменения состояния через observable events у AbstractControl (FormControl, FormGroup, FormArray и FormRecord). Этот поток реагирует на изменения значения, статуса, состояний pristine, touched и reset, а также на действия уровня формы, например submit. Все обновления можно обработать одной подпиской, не связывая несколько observable.
import{Component}from'@angular/core';import{FormControl,ValueChangeEvent,StatusChangeEvent,PristineChangeEvent,TouchedChangeEvent,FormResetEvent,FormSubmittedEvent,ReactiveFormsModule,FormGroup,}from'@angular/forms';@Component(/* ... */)exportclassUnifiedEventsBasicComponent{form=newFormGroup({username:newFormControl(''),});constructor(){this.form.events.subscribe((e)=>{if(einstanceofValueChangeEvent){console.log('Value changed to: ',e.value);}if(einstanceofStatusChangeEvent){console.log('Status changed to: ',e.status);}if(einstanceofPristineChangeEvent){console.log('Pristine status changed to: ',e.pristine);}if(einstanceofTouchedChangeEvent){console.log('Touched status changed to: ',e.touched);}if(einstanceofFormResetEvent){console.log('Form was reset');}if(einstanceofFormSubmittedEvent){console.log('Form was submitted');}});}}
При изменении значения событие уходит сразу после обновления значения этого элемента. Значение родителя (например, если этот FormControl входит в FormGroup) обновляется позже, поэтому чтение значения родителя (через свойство value) из обработчика этого события может вернуть ещё не обновлённое значение. Подпишитесь на events родительского элемента.
Реактивные формы ведут состояние через touched/untouched и pristine/dirty. Angular обновляет их сам при взаимодействии с DOM, но ими можно управлять и из кода.
markAsTouched — помечает элемент или форму как touched по событиям фокуса и потери фокуса, которые не меняют значение. По умолчанию состояние поднимается к родительским элементам.
12345
// Show validation errors after user leaves a fieldonEmailBlur(){constemail=this.form.get('email');email.markAsTouched();}
markAsUntouched — помечает элемент или форму как untouched. Спускается ко всем дочерним элементам и заново считает статус touched у всех родителей.
12345
// Reset form state after successful submissiononSubmitSuccess(){this.form.markAsUntouched();this.form.markAsPristine();}
markAsDirty — помечает элемент или форму как dirty: значение изменилось. По умолчанию состояние поднимается к родительским элементам.
123456
// Mark programmatically changed values as modifiedautofillAddress(){constpreviousAddress=getAddress();this.form.patchValue(previousAddress,{emitEvent:false});this.form.markAsDirty();}
markAsPristine — помечает элемент или форму как pristine. Помечает все дочерние элементы как pristine и заново считает статус pristine у всех родителей.
123456
// Reset pristine state after saving to track new changessaveForm(){this.api.save(this.form.value).subscribe(()=>{this.form.markAsPristine();});}
markAllAsDirty — помечает элемент или форму и всех потомков как dirty.
12345
// Mark imported data as dirtyloadData(data:FormData){this.form.patchValue(data);this.form.markAllAsDirty();}
markAllAsTouched — помечает элемент или форму и всех потомков как touched. Удобно, чтобы показать ошибки проверки по всей форме.
12345678
// Show all validation errors before submissiononSubmit(){if(this.form.invalid){this.form.markAllAsTouched();return;}this.saveForm();}
Управление порождением и распространением событий¶
Когда элементы управления обновляют из кода, можно точно задать, как изменения идут по иерархии формы и порождаются ли события.
По умолчанию emitEvent: true: любое изменение элемента порождает события в observable valueChanges и statusChanges. Значение emitEvent: false подавляет эти события. Это полезно, когда значение задают из кода и не хотят запускать реактивное поведение вроде автосохранения, когда нужно избежать циклических обновлений между элементами или когда при массовом обновлении событие должно уйти один раз в конце.
1 2 3 4 5 6 7 8 910111213141516171819
@Component({/* ... */})exportclassBlogPostEditor{postForm=newFormGroup({title:newFormControl(''),content:newFormControl(''),});constructor(){// Auto-save draft every time user typesthis.postForm.valueChanges.subscribe((formValue)=>{this.autosaveDraft(formValue);});}loadExistingDraft(savedDraft:{title:string;content:string}){// Restore draft without triggering auto-savethis.postForm.setValue(savedDraft,{emitEvent:false});}}
По умолчанию onlySelf: false: обновления поднимаются к родительским элементам и заново считают их значения и статус проверки. Значение onlySelf: true оставляет обновление на текущем элементе и не уведомляет родителя. Это удобно в пакетных операциях, когда родительское обновление запускают вручную один раз.
Вспомогательные функции сужения типа элемента управления¶
В Angular есть четыре вспомогательные функции: они определяют конкретный тип AbstractControl. Эти функции работают как охранники типа и сужают тип элемента, когда возвращают true. Тогда внутри того же блока можно безопасно обращаться к свойствам конкретного подтипа.
Вспомогательная функция
Подробности
isFormControl
Возвращает true, если элемент — FormControl.
isFormGroup
Возвращает true, если элемент — FormGroup.
isFormRecord
Возвращает true, если элемент — FormRecord.
isFormArray
Возвращает true, если элемент — FormArray.
Особенно полезны эти функции в своих валидаторах: сигнатура получает AbstractControl, а логика рассчитана на конкретный вид элемента.
1 2 3 4 5 6 7 8 91011
import{AbstractControl,isFormArray}from'@angular/forms';exportfunctionpositiveValues(control:AbstractControl){if(!isFormArray(control)){returnnull;// Not a FormArray: validator is not applicable.}// Safe to access FormArray-specific API after narrowing.consthasNegative=control.controls.some((c)=>c.value<0);returnhasNegative?{positiveValues:true}:null;}
В таблице ниже — базовые классы и сервисы, которыми создают элементы реактивных форм и управляют ими. Полный синтаксис — в справочнике API пакета Forms.
Абстрактный базовый класс для конкретных классов FormControl, FormGroup и FormArray. Задаёт их общее поведение и свойства.
FormControl
Ведёт значение и статус валидности отдельного элемента управления. Соответствует HTML-элементу формы, например <input> или <select>.
FormGroup
Ведёт значение и состояние валидности группы экземпляров AbstractControl. В свойства группы входят дочерние элементы. Форма верхнего уровня в компоненте — это FormGroup.
FormArray
Ведёт значение и состояние валидности массива экземпляров AbstractControl с числовыми индексами.
FormBuilder
Внедряемый сервис с фабричными методами для создания экземпляров элементов управления.
FormRecord
Отслеживает значение и состояние валидности набора экземпляров FormControl с одним и тем же типом значения.