Реактивные формы
Реактивные формы позволяют использовать подход, основанный на модели, для обработки вводимых в форму данных, значения которых меняются со временем. В этом руководстве показано, как создать и обновить базовый элемент управления формы, использовать несколько элементов управления в группе, проверять значения формы и создавать динамические формы, в которых можно добавлять или удалять элементы управления во время выполнения.
Обзор реактивных форм
В реактивных формах используется явный и неизменяемый подход к управлению состоянием формы в определённый момент времени. Каждое изменение состояния формы возвращает новое состояние, которое сохраняет целостность модели между изменениями. Реактивные формы построены на основе потоков наблюдаемых объектов: данные полей ввода и их значения предоставляются в виде потоков, к которым можно обращаться синхронно.
Реактивные формы также упрощают тестирование, поскольку при запросе данные гарантированно согласованы и предсказуемы. Любые потребители этих потоков могут безопасно обрабатывать данные.
Реактивные формы отличаются от форм на основе шаблонов. Реактивные формы обеспечивают синхронный доступ к модели данных, неизменяемость с помощью операторов наблюдаемых объектов и отслеживание изменений через потоки наблюдаемых объектов.
Формы на основе шаблонов позволяют напрямую изменять данные в шаблоне, но они менее явные, чем реактивные формы, поскольку полагаются на директивы, встроенные в шаблон, и изменяемые данные для асинхронного отслеживания изменений. Подробное сравнение двух подходов см. в разделе Обзор форм.
Добавление базового элемента управления формы
Для использования элементов управления формы нужно выполнить три шага.
- Создайте новый компонент и зарегистрируйте модуль реактивных форм. Этот модуль объявляет директивы реактивных форм, необходимые для их использования.
- Создайте экземпляр
FormControl. - Зарегистрируйте
FormControlв шаблоне.
Затем можно отобразить форму, добавив компонент в шаблон.
В следующих примерах показано, как добавить один элемент управления формы. В примере пользователь вводит своё имя в поле ввода, приложение получает это значение и отображает текущее значение элемента управления формы.
-
Создание нового компонента и импорт ReactiveFormsModule
Используйте команду CLI
ng generate component, чтобы создать компонент в проекте, импортируйтеReactiveFormsModuleиз пакета@angular/formsи добавьте его в массивimportsкомпонента.import {FormControl, ReactiveFormsModule} from '@angular/forms'; @Component({ selector: 'app-name-editor', templateUrl: './name-editor.component.html', styleUrls: ['./name-editor.component.css'], imports: [ReactiveFormsModule], }) export class NameEditorComponent { -
Объявление экземпляра FormControl
Используйте конструктор
FormControl, чтобы задать начальное значение — в данном случае пустую строку. Создавая эти элементы управления в классе компонента, вы получаете немедленный доступ к отслеживанию, обновлению и проверке состояния поля ввода формы.import {Component} from '@angular/core'; import {FormControl, ReactiveFormsModule} from '@angular/forms'; @Component({ selector: 'app-name-editor', templateUrl: './name-editor.component.html', styleUrls: ['./name-editor.component.css'], imports: [ReactiveFormsModule], }) export class NameEditorComponent { name = new FormControl(''); } -
Регистрация элемента управления в шаблоне
После создания элемента управления в классе компонента необходимо связать его с элементом управления формы в шаблоне. Обновите шаблон, добавив привязку элемента управления формы с помощью
formControl, предоставляемойFormControlDirective, которая также входит вReactiveFormsModule.<label for="name">Name: </label> <input id="name" type="text" [formControl]="name" />
Теперь с помощью синтаксиса привязки шаблона элемент управления формы зарегистрирован для элемента ввода
nameв шаблоне. Элемент управления формы и элемент DOM взаимодействуют друг с другом: представление отражает изменения модели, а модель — изменения представления. -
Отображение компонента
FormControl, присвоенный свойствуname, отображается при добавлении компонента<app-name-editor>в шаблон.<app-name-editor />
Отображение значения элемента управления формы
Отобразить значение можно следующими способами:
- С помощью наблюдаемого объекта
valueChanges, который позволяет отслеживать изменения значения формы в шаблоне с помощьюAsyncPipeили в классе компонента с помощью методаsubscribe() - С помощью свойства
value, которое возвращает снимок текущего значения
В следующем примере показано, как отобразить текущее значение с помощью интерполяции в шаблоне.
<p>Value: {{ name.value }}</p>
Отображаемое значение меняется при обновлении элемента управления формы.
Реактивные формы предоставляют доступ к сведениям об элементе управления через свойства и методы каждого экземпляра. Эти свойства и методы базового класса AbstractControl используются для управления состоянием формы и определения момента отображения сообщений при обработке проверки введённых данных.
Информацию о других свойствах и методах FormControl см. в справочнике по API.
Замена значения элемента управления формы
В реактивных формах есть методы для программного изменения значения элемента управления, что позволяет обновлять его без взаимодействия с пользователем. Экземпляр элемента управления формы предоставляет метод setValue(), который обновляет значение элемента управления формы и проверяет структуру переданного значения на соответствие структуре элемента управления. Например, при получении данных формы из серверного API или службы используйте метод setValue(), чтобы задать элементу управления новое значение, полностью заменив старое.
В следующем примере в класс компонента добавляется метод, который обновляет значение элемента управления на Nancy с помощью метода setValue().
updateName() {
this.name.setValue('Nancy');
}
Обновите шаблон, добавив кнопку для имитации обновления имени. При нажатии кнопки Обновить имя введённое в элемент управления формы значение отображается как текущее.
<button type="button" (click)="updateName()">Update Name</button>
Модель формы является источником достоверных данных для элемента управления, поэтому при нажатии кнопки значение поля ввода меняется в классе компонента и заменяет текущее значение.
ПОЛЕЗНО: В этом примере используется один элемент управления. При использовании метода setValue() с экземпляром группы формы или массива формы значение должно соответствовать структуре группы или массива.
Группировка элементов управления формы
Формы обычно содержат несколько связанных элементов управления. В реактивных формах предусмотрено два способа объединить несколько связанных элементов управления в одну форму ввода.
| Группы форм | Сведения |
|---|---|
| Группа формы | Определяет форму с фиксированным набором элементов управления, которыми можно управлять совместно. Основы работы с группами форм рассматриваются в этом разделе. Кроме того, можно вкладывать группы форм для создания более сложных форм. |
| Массив формы | Определяет динамическую форму, в которую можно добавлять и из которой можно удалять элементы управления во время выполнения. Можно также вкладывать массивы форм для создания более сложных форм. Подробнее об этом варианте см. в разделе Создание динамических форм. |
Как экземпляр элемента управления формы позволяет управлять одним полем ввода, так и экземпляр группы формы отслеживает состояние группы экземпляров элементов управления формы (например, формы). При создании группы формы каждый элемент управления в её экземпляре отслеживается по имени. В следующем примере показано, как управлять несколькими экземплярами элементов управления формы в одной группе.
Создайте компонент ProfileEditor и импортируйте классы FormGroup и FormControl из пакета @angular/forms.
ng generate component ProfileEditor
import {FormGroup, FormControl, ReactiveFormsModule} from '@angular/forms';
@Component({
selector: 'app-profile-editor',
templateUrl: './profile-editor.component.html',
styleUrls: ['./profile-editor.component.css'],
imports: [ReactiveFormsModule],
})
export class ProfileEditorComponent {
Чтобы добавить группу формы в этот компонент, выполните следующие действия.
-
Создание экземпляра FormGroup
Создайте в классе компонента свойство с именем
profileFormи присвойте ему новый экземпляр группы формы. Чтобы инициализировать группу формы, передайте конструктору объект с именованными ключами, сопоставленными с соответствующими элементами управления.Для формы профиля добавьте два экземпляра элемента управления формы с именами
firstNameиlastNameimport {Component} from '@angular/core'; import {FormGroup, FormControl, ReactiveFormsModule} from '@angular/forms'; @Component({ selector: 'app-profile-editor', templateUrl: './profile-editor.component.html', styleUrls: ['./profile-editor.component.css'], imports: [ReactiveFormsModule], }) export class ProfileEditorComponent { profileForm = new FormGroup({ firstName: new FormControl(''), lastName: new FormControl(''), }); }Теперь отдельные элементы управления формы объединены в группу. Экземпляр
FormGroupпредоставляет значение модели в виде объекта, сформированного из значений каждого элемента управления в группе. Экземпляр группы формы имеет те же свойства (например,valueиuntouched) и методы (например,setValue()), что и экземпляр элемента управления формы. -
Связывание модели FormGroup с представлением
Группа формы отслеживает состояние и изменения каждого элемента управления, поэтому при изменении одного из них родительский элемент управления также передаёт новое состояние или значение. Модель группы формируется на основе её элементов. После определения модели необходимо обновить шаблон, чтобы отразить модель в представлении.
<form [formGroup]="profileForm"> <label for="first-name">First Name: </label> <input id="first-name" type="text" formControlName="firstName" /> <label for="last-name">Last Name: </label> <input id="last-name" type="text" formControlName="lastName" /> </form>
Как группа формы содержит набор элементов управления, так и
FormGroupprofileForm привязывается к элементуformс помощью директивыFormGroup, которая создаёт уровень взаимодействия между моделью и формой, содержащей поля ввода. ВводformControlName, предоставляемый директивойFormControlName, связывает каждый отдельный элемент ввода с элементом управления формы, определённым вFormGroup. Элементы управления формы взаимодействуют с соответствующими элементами. Они также передают изменения экземпляру группы формы, который служит источником достоверного значения модели. -
Сохранение данных формы
Компонент
ProfileEditorпринимает данные от пользователя, но в реальном приложении необходимо получить значение формы и сделать его доступным для дальнейшей обработки за пределами компонента. ДирективаFormGroupотслеживает событиеsubmit, отправляемое элементомform, и генерирует событиеngSubmit, которое можно связать с функцией обратного вызова. Добавьте к тегуformпрослушиватель событияngSubmitс методом обратного вызоваonSubmit().<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
Метод
onSubmit()компонентаProfileEditorполучает текущее значениеprofileForm. Используйтеoutput(), чтобы инкапсулировать форму и предоставлять её значение за пределами компонента. В следующем примереconsole.warnиспользуется для вывода сообщения в консоль браузера.onSubmit() { // TODO: Use output() with form value console.warn(this.profileForm.value); }Событие
submitгенерируется тегомformс помощью встроенного события DOM. Чтобы вызвать это событие, нажмите кнопку типаsubmit. Это позволяет пользователю отправить заполненную форму клавишей Enter.Добавьте кнопку в нижнюю часть формы с помощью элемента
button, чтобы отправлять форму.<p>Complete the form to enable button.</p> <button type="submit" [disabled]="!profileForm.valid">Submit</button>
В кнопке из предыдущего фрагмента также есть привязка
disabled, которая отключает кнопку, когдаprofileFormнедействительна. Пока проверка не выполняется, кнопка всегда включена. Основы проверки форм рассматриваются в разделе Проверка вводимых данных формы. -
Отображение компонента
Чтобы отобразить компонент
ProfileEditor, содержащий форму, добавьте его в шаблон компонента.<app-profile-editor />
ProfileEditorпозволяет управлять экземплярами элементов управления формы для элементов управленияfirstNameиlastNameв экземпляре группы формы.Создание вложенных групп форм
Группы форм могут содержать в качестве дочерних элементов как отдельные экземпляры элементов управления формы, так и другие экземпляры групп форм. Это упрощает поддержку составных моделей форм и позволяет логически группировать их элементы.
При создании сложных форм проще управлять разными областями данных, разбив их на небольшие разделы. Вложенный экземпляр группы формы позволяет разделить большие группы форм на более компактные и удобные в управлении.
Чтобы создать более сложную форму, выполните следующие действия.
- Создайте вложенную группу.
- Сгруппируйте вложенную форму в шаблоне.
Некоторые виды информации естественным образом объединяются в группы. Имя и адрес — типичные примеры таких вложенных групп, которые используются в следующих примерах.
Чтобы создать вложенную группу в `profileForm`, добавьте вложенный элемент `address` в экземпляр группы формы. import {Component} from '@angular/core'; import {FormGroup, FormControl, ReactiveFormsModule} from '@angular/forms'; @Component({ selector: 'app-profile-editor', templateUrl: './profile-editor.component.html', styleUrls: ['./profile-editor.component.css'], imports: [ReactiveFormsModule], }) export class ProfileEditorComponent { profileForm = new FormGroup({ firstName: new FormControl(''), lastName: new FormControl(''), address: new FormGroup({ street: new FormControl(''), city: new FormControl(''), state: new FormControl(''), zip: new FormControl(''), }), }); }В этом примере
address groupобъединяет существующие элементы управленияfirstNameиlastNameс новыми элементами управленияstreet,city,stateиzip. Хотя элементaddressв группе формы является дочерним по отношению к общему элементуprofileFormв группе формы, к изменениям значения и состояния применяются те же правила. Изменения состояния и значения вложенной группы форм передаются родительской группе формы, сохраняя согласованность с общей моделью. -
Группировка вложенной формы в шаблоне
После обновления модели в классе компонента обновите шаблон, чтобы связать экземпляр группы формы с её элементами ввода. Добавьте группу форм
addressс полямиstreet,city,stateиzipв шаблонProfileEditor.<div formGroupName="address"> <h2>Address</h2> <label for="street">Street: </label> <input id="street" type="text" formControlName="street" /> <label for="city">City: </label> <input id="city" type="text" formControlName="city" /> <label for="state">State: </label> <input id="state" type="text" formControlName="state" /> <label for="zip">Zip Code: </label> <input id="zip" type="text" formControlName="zip" /> </div>Форма
ProfileEditorотображается как одна группа, но модель разбита на части, представляющие логически сгруппированные области.Отобразите значение экземпляра группы формы в шаблоне компонента с помощью свойства
valueиJsonPipe.
Обновление частей модели данных
При обновлении значения экземпляра группы формы, содержащего несколько элементов управления, может потребоваться изменить только часть модели. В этом разделе рассказывается, как обновлять определённые части модели данных элемента управления формы.
Есть два способа обновить значение модели:
| Методы | Сведения |
|---|---|
setValue() | Задаёт новое значение отдельному элементу управления. Метод setValue() строго соблюдает структуру группы формы и полностью заменяет значение элемента управления. |
patchValue() | Заменяет в модели формы все свойства объекта, которые были изменены. |
Строгие проверки метода setValue() помогают обнаруживать ошибки вложения в сложных формах, тогда как patchValue() не сообщает об этих ошибках.
В ProfileEditorComponent используйте метод updateProfile, как показано в следующем примере, чтобы обновить имя пользователя и адрес улицы.
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street',
},
});
}
Имитируйте обновление, добавив в шаблон кнопку для обновления профиля пользователя по запросу.
<button type="button" (click)="updateProfile()">Update Profile</button>
При нажатии кнопки модель profileForm обновляется новыми значениями для firstName и street. Обратите внимание: street передаётся в объекте внутри свойства address. Это необходимо, поскольку метод patchValue() применяет обновление в соответствии со структурой модели. patchValue() обновляет только свойства, определённые в модели формы.
Создание элементов управления с помощью службы FormBuilder
Ручное создание экземпляров элементов управления формы может стать однообразной задачей при работе с несколькими формами. Служба FormBuilder предоставляет удобные методы для создания элементов управления.
Чтобы воспользоваться этой службой, выполните следующие действия.
- Импортируйте класс
FormBuilder. - Внедрите службу
FormBuilder. - Создайте содержимое формы.
В следующих примерах показано, как переработать компонент ProfileEditor, чтобы использовать службу построителя форм для создания экземпляров элементов управления и групп форм.
-
Импорт класса FormBuilder
Импортируйте класс
FormBuilderиз пакета@angular/forms.import {FormBuilder, ReactiveFormsModule} from '@angular/forms'; -
Внедрение службы FormBuilder
Служба
FormBuilder— это внедряемый провайдер из модуля реактивных форм. Используйте функциюinject(), чтобы внедрить эту зависимость в компонент.private formBuilder = inject(FormBuilder);
-
Создание элементов управления формы
Служба
FormBuilderпредоставляет три метода:control(),group()иarray(). Это фабричные методы для создания экземпляров элементов управления формы, групп форм и массивов форм в классах компонентов. Используйте методgroup, чтобы создать элементы управленияprofileForm.profileForm = this.formBuilder.group({ firstName: [''], lastName: [''], address: this.formBuilder.group({ street: [''], city: [''], state: [''], zip: [''], }), });В предыдущем примере метод
group()используется с тем же объектом для определения свойств модели. Значение для каждого имени элемента управления представляет собой массив, первым элементом которого является начальное значение.СОВЕТ: Элемент управления можно определить, указав только начальное значение. Однако если для элементов управления требуется синхронная или асинхронная проверка, добавьте синхронные и асинхронные валидаторы в качестве второго и третьего элементов массива. Сравните использование построителя форм с созданием экземпляров вручную.
Проверка вводимых данных формы
Проверка формы используется, чтобы убедиться в полноте и правильности введённых пользователем данных. В этом разделе рассматривается добавление одного валидатора к элементу управления формы и отображение общего состояния формы. Более подробно проверка форм описана в руководстве Проверка форм.
Чтобы добавить проверку формы, выполните следующие действия.
- Импортируйте функцию-валидатор в компонент формы.
- Добавьте валидатор к полю формы.
- Добавьте логику обработки состояния проверки.
Самая распространённая проверка — обязательность заполнения поля. В следующем примере показано, как добавить проверку обязательного заполнения для элемента управления firstName и отобразить результат проверки.
-
Импорт функции-валидатора
В реактивные формы входят функции-валидаторы для распространённых сценариев. Эти функции получают элемент управления, который необходимо проверить, и возвращают объект ошибки или значение null в зависимости от результата проверки.
Импортируйте класс
Validatorsиз пакета@angular/forms.import {Validators} from '@angular/forms'; -
Установка обязательного поля
В компоненте
ProfileEditorдобавьте статический методValidators.requiredв качестве второго элемента массива для элемента управленияfirstName.private formBuilder = inject(FormBuilder); profileForm = this.formBuilder.group({ firstName: ['', Validators.required], lastName: [''], address: this.formBuilder.group({ street: [''], city: [''], state: [''], zip: [''], }), }); -
Отображение состояния формы
После добавления обязательного поля к элементу управления формы его начальное состояние будет недействительным. Это недействительное состояние передаётся родительскому элементу группы формы, в результате чего его состояние также становится недействительным. Текущее состояние экземпляра группы формы доступно через свойство
status.Отобразите текущее состояние
profileFormс помощью интерполяции.<p>Form Status: {{ profileForm.status }}</p>Кнопка Отправить отключена, потому что
profileFormнедействительна из-за обязательного элемента управления формыfirstName. После заполнения поля вводаfirstNameформа станет действительной, а кнопка Отправить включится.Подробнее о проверке форм см. в руководстве Проверка форм.
Создание динамических форм
FormArray — это альтернатива FormGroup для управления любым количеством элементов управления без имён. Как и в экземплярах групп форм, в экземплярах массивов форм можно динамически добавлять и удалять элементы управления, а значение и состояние проверки экземпляра массива формы вычисляются на основе его дочерних элементов управления. Однако не нужно задавать имя для каждого элемента управления, поэтому этот вариант отлично подходит, если заранее неизвестно количество дочерних значений.
Чтобы определить динамическую форму, выполните следующие действия.
- Импортируйте класс
FormArray. - Определите элемент управления
FormArray. - Получите доступ к элементу управления
FormArrayс помощью метода-геттера. - Отобразите массив формы в шаблоне.
В следующем примере показано, как управлять массивом псевдонимов в ProfileEditor.
-
Импорт класса FormArray
Импортируйте класс
FormArrayиз@angular/forms, чтобы использовать его для указания типов. СлужбаFormBuilderготова создать экземплярFormArray.import {FormArray} from '@angular/forms'; -
Определение элемента управления FormArray
Можно инициализировать массив формы любым количеством элементов управления — от нуля до нескольких, — определив их в массиве. Добавьте свойство
aliasesв экземпляр группы формы дляprofileForm, чтобы определить массив формы.Используйте метод
FormBuilder.array(), чтобы определить массив, и методFormBuilder.control(), чтобы заполнить его начальным элементом управления.private formBuilder = inject(FormBuilder); profileForm = this.formBuilder.group({ firstName: ['', Validators.required], lastName: [''], address: this.formBuilder.group({ street: [''], city: [''], state: [''], zip: [''], }), aliases: this.formBuilder.array([this.formBuilder.control('')]), });Теперь элемент управления псевдонимов в экземпляре группы формы содержит один элемент управления, пока в него динамически не будут добавлены другие.
-
Доступ к элементу управления FormArray
Геттер предоставляет доступ к псевдонимам в экземпляре массива формы и избавляет от необходимости многократно вызывать метод
profileForm.get()для получения каждого экземпляра. Экземпляр массива формы представляет собой массив с неопределённым количеством элементов управления. Удобно получать доступ к элементу управления через геттер; этот подход легко повторить для дополнительных элементов управления.Используйте синтаксис геттера, чтобы создать свойство класса
aliasesдля получения элемента управления массива формы псевдонимов из родительской группы формы.get aliases() { return this.profileForm.get('aliases') as FormArray; }Поскольку возвращаемый элемент управления имеет тип
AbstractControl, для доступа к синтаксису методов экземпляра массива формы необходимо явно указать тип. Определите метод для динамической вставки элемента управления псевдонима в массив формы псевдонимов. МетодFormArray.push()вставляет элемент управления в массив как новый элемент. Кроме того, в FormArray.push() можно передать массив элементов управления, чтобы зарегистрировать сразу несколько.addAlias() { this.aliases.push(this.formBuilder.control('')); }В шаблоне каждый элемент управления отображается как отдельное поле ввода.
-
Отображение массива формы в шаблоне
Чтобы добавить псевдонимы из модели формы, необходимо включить их в шаблон. Подобно элементу ввода
formGroupName, предоставляемомуFormGroupNameDirective,formArrayNameсвязывает экземпляр массива формы с шаблоном с помощьюFormArrayNameDirective.Добавьте следующий HTML-код шаблона после
<div>, закрывающего элементformGroupName.<div formArrayName="aliases"> <h2>Aliases</h2> <button type="button" (click)="addAlias()">+ Add another alias</button> @for (alias of aliases.controls; track $index; let i = $index) { <div> <!-- The repeated alias template --> <label for="alias-{{ i }}">Alias:</label> <input id="alias-{{ i }}" type="text" [formControlName]="i" /> </div> } </div>Блок
@forперебирает каждый экземпляр элемента управления формы, предоставленный массивом формы псевдонимов. Поскольку у элементов массива формы нет имён, индекс присваивается переменнойi, а затем передаётся каждому элементу управления, чтобы связать его с полем вводаformControlName.При добавлении нового экземпляра псевдонима он получает элемент управления из нового экземпляра массива формы по индексу. Это позволяет отслеживать каждый отдельный элемент управления при вычислении состояния и значения корневого элемента управления.
ПРИМЕЧАНИЕ: В приложениях без зон изменение модели реактивной формы (например, вызов
FormArray.push()) не запускает проверку изменений компонента автоматически. Если шаблон зависит от структурных изменений модели, таких какaliases.controls, убедитесь, что компонент уведомляет Angular о необходимости запустить проверку изменений, например, связав наблюдаемый объект формы сChangeDetectorRef.markForCheck():import {ChangeDetectorRef, Component, inject} from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; @Component({ /* ... */ }) export class ProfileEditor { private readonly cdr = inject(ChangeDetectorRef); constructor() { this.profileForm.valueChanges .pipe(takeUntilDestroyed()) .subscribe(() => this.cdr.markForCheck()); } } -
Добавление псевдонима
Изначально форма содержит одно поле
Alias. Чтобы добавить ещё одно поле, нажмите кнопку Добавить псевдоним. Можно также проверить массив псевдонимов, предоставленный моделью формы и отображаемый с помощьюForm Valueв нижней части шаблона. Вместо экземпляра элемента управления формы для каждого псевдонима можно создать другой экземпляр группы формы с дополнительными полями. Процесс определения элемента управления для каждого элемента остаётся тем же.
Использование FormArrayDirective для массивов форм верхнего уровня
С помощью FormArrayDirective можно напрямую привязать FormArray к элементу <form>.
Это полезно, если в форме не используется группа FormGroup верхнего уровня и сам массив представляет всю модель формы.
import {Component} from '@angular/core';
import {FormArray, FormControl} from '@angular/forms';
@Component({
selector: 'form-array-example',
template: `
<form [formArray]="form">
@for (control of form.controls; track $index) {
<input [formControlName]="$index" />
}
</form>
`,
})
export class FormArrayExampleComponent {
controls = [new FormControl('fish'), new FormControl('cat'), new FormControl('dog')];
form = new FormArray(this.controls);
} Унифицированные события изменения состояния элементов управления
Все элементы управления формы предоставляют единый поток событий изменения состояния элементов управления через наблюдаемый объект events класса AbstractControl (FormControl, FormGroup, FormArray и FormRecord). Этот единый поток позволяет реагировать на изменения состояния значения, статуса, чистоты, посещения и сброса, а также на действия на уровне формы, например отправку. Это позволяет обрабатывать все обновления с помощью одной подписки, вместо того чтобы настраивать несколько наблюдаемых объектов.
Типы событий
Каждый элемент, выдаваемый events, является экземпляром определённого класса события:
-
ValueChangeEvent— при изменении значения элемента управления. -
StatusChangeEvent— при обновлении статуса проверки элемента управления до одного из значенийFormControlStatus(VALID,INVALID,PENDINGилиDISABLED). -
PristineChangeEvent— при изменении состояния pristine/dirty элемента управления. -
TouchedChangeEvent— при изменении состояния touched/untouched элемента управления. -
FormResetEvent— при сбросе элемента управления или формы через APIreset()либо с помощью стандартного действия. -
FormSubmittedEvent— при отправке формы.
Все классы событий наследуются от ControlEvent и содержат ссылку source на AbstractControl, который инициировал изменение. Это полезно при работе с большими формами.
import {Component} from '@angular/core';
import {
FormControl,
ValueChangeEvent,
StatusChangeEvent,
PristineChangeEvent,
TouchedChangeEvent,
FormResetEvent,
FormSubmittedEvent,
ReactiveFormsModule,
FormGroup,
} from '@angular/forms';
@Component({
/*...*/
})
export class UnifiedEventsBasicComponent {
form = new FormGroup({
username: new FormControl(''),
});
constructor() {
this.form.events.subscribe((e) => {
if (e instanceof ValueChangeEvent) {
console.log('Value changed to: ', e.value);
}
if (e instanceof StatusChangeEvent) {
console.log('Status changed to: ', e.status);
}
if (e instanceof PristineChangeEvent) {
console.log('Pristine status changed to: ', e.pristine);
}
if (e instanceof TouchedChangeEvent) {
console.log('Touched status changed to: ', e.touched);
}
if (e instanceof FormResetEvent) {
console.log('Form was reset');
}
if (e instanceof FormSubmittedEvent) {
console.log('Form was submitted');
}
});
}
} Фильтрация определенных событий
Если вам нужны только определенные типы событий, используйте операторы RxJS.
import {filter} from 'rxjs/operators';
import {StatusChangeEvent} from '@angular/forms';
control.events
.pipe(filter((e) => e instanceof StatusChangeEvent))
.subscribe((e) => console.log('Status:', e.status)); Объединение нескольких подписок
До
import {combineLatest} from 'rxjs/operators';
combineLatest([control.valueChanges, control.statusChanges]).subscribe(([value, status]) => {
/* ... */
});
После
control.events.subscribe((e) => {
// Handle ValueChangeEvent, StatusChangeEvent, etc.
});
ПРИМЕЧАНИЕ: При изменении значения событие отправляется сразу после обновления значения этого элемента управления. Значение родительского элемента управления (например, если этот FormControl является частью FormGroup) обновляется позже, поэтому при обращении к значению родительского элемента управления (с помощью свойства value) из обработчика этого события можно получить еще не обновленное значение. Вместо этого подпишитесь на events родительского элемента управления.
Управление состоянием элементов управления формы
Реактивные формы отслеживают состояние элементов управления с помощью состояний touched/untouched и pristine/dirty. Angular обновляет их автоматически при взаимодействии с DOM, но вы также можете управлять ими программно.
markAsTouched — отмечает элемент управления или форму как touched после событий фокусировки и потери фокуса, не изменяющих значение. По умолчанию изменение распространяется на родительские элементы управления.
// Show validation errors after user leaves a field
onEmailBlur() {
const email = this.form.get('email');
email.markAsTouched();
}
markAsUntouched — отмечает элемент управления или форму как untouched. Изменение распространяется на все дочерние элементы управления и пересчитывает состояние touched всех родительских элементов управления.
// Reset form state after successful submission
onSubmitSuccess() {
this.form.markAsUntouched();
this.form.markAsPristine();
}
markAsDirty — отмечает элемент управления или форму как dirty, то есть указывает, что значение было изменено. По умолчанию изменение распространяется на родительские элементы управления.
// Mark programmatically changed values as modified
autofillAddress() {
const previousAddress = getAddress();
this.form.patchValue(previousAddress, { emitEvent: false });
this.form.markAsDirty();
}
markAsPristine — отмечает элемент управления или форму как pristine. Отмечает все дочерние элементы управления как pristine и пересчитывает состояние pristine всех родительских элементов управления.
// Reset pristine state after saving to track new changes
saveForm() {
this.api.save(this.form.value).subscribe(() => {
this.form.markAsPristine();
});
}
markAllAsDirty — отмечает элемент управления или форму и все дочерние элементы управления как dirty.
// Mark imported data as dirty
loadData(data: FormData) {
this.form.patchValue(data);
this.form.markAllAsDirty();
}
markAllAsTouched — отмечает элемент управления или форму и все дочерние элементы управления как touched. Полезно для отображения ошибок проверки во всей форме.
// Show all validation errors before submission
onSubmit() {
if (this.form.invalid) {
this.form.markAllAsTouched();
return;
}
this.saveForm();
} Управление отправкой и распространением событий
При программном обновлении элементов управления формы можно точно задать, как изменения распространяются по иерархии формы и отправляются ли события.
Отправка событий
По умолчанию emitEvent: true любое изменение элемента управления отправляет события через наблюдаемые объекты valueChanges и statusChanges. Установка emitEvent: false подавляет отправку этих событий. Это полезно, когда значения задаются программно без запуска реактивного поведения, например автосохранения, когда нужно избежать циклических обновлений между элементами управления или выполнить массовые обновления, отправив события только один раз в конце.
@Component({
/* ... */
})
export class BlogPostEditor {
postForm = new FormGroup({
title: new FormControl(''),
content: new FormControl(''),
});
constructor() {
// Auto-save draft every time user types
this.postForm.valueChanges.subscribe((formValue) => {
this.autosaveDraft(formValue);
});
}
loadExistingDraft(savedDraft: {title: string; content: string}) {
// Restore draft without triggering auto-save
this.postForm.setValue(savedDraft, {emitEvent: false});
}
} Управление распространением изменений
По умолчанию onlySelf: false обновления распространяются на родительские элементы управления, пересчитывая их значения и статус проверки. Установка onlySelf: true ограничивает обновление текущим элементом управления и не уведомляет родительские элементы. Это полезно при пакетных операциях, когда нужно вручную обновить родительский элемент один раз.
updatePostalCodeValidator(country: string) {
const postal = this.addressForm.get('postalCode');
const validators = country === 'US'
? [Validators.maxLength(5)]
: [Validators.maxLength(7)];
postal.setValidators(validators);
postal.updateValueAndValidity({ onlySelf: true, emitEvent: false });
}
ПОЛЕЗНО: Сведения о динамическом управлении валидаторами во время выполнения см. в разделе Динамическое управление валидаторами в реактивных формах руководства по проверке форм.
Вспомогательные функции для уточнения типов элементов управления формы
Angular предоставляет четыре вспомогательные функции, которые помогают определить конкретный тип AbstractControl. Эти функции служат проверками типов и уточняют тип элемента управления, если возвращают true, позволяя безопасно обращаться к свойствам конкретного подтипа в том же блоке.
| Вспомогательная функция | Описание |
|---|---|
isFormControl | Возвращает true, если элемент управления является FormControl. |
isFormGroup | Возвращает true, если элемент управления является FormGroup
|
isFormRecord | Возвращает true, если элемент управления является FormRecord
|
isFormArray | Возвращает true, если элемент управления является FormArray
|
Эти вспомогательные функции особенно полезны в пользовательских валидаторах, где функция получает AbstractControl, но логика предназначена для конкретного типа элемента управления.
import {AbstractControl, isFormArray} from '@angular/forms';
export function positiveValues(control: AbstractControl) {
if (!isFormArray(control)) {
return null; // Not a FormArray: validator is not applicable.
}
// Safe to access FormArray-specific API after narrowing.
const hasNegative = control.controls.some((c) => c.value < 0);
return hasNegative ? {positiveValues: true} : null;
} Сводка API реактивных форм
В следующей таблице перечислены базовые классы и сервисы, используемые для создания элементов управления реактивных форм и управления ими. Полный синтаксис см. в справочной документации API для пакета Forms.
Классы
| Класс | Описание |
|---|---|
AbstractControl | Абстрактный базовый класс конкретных классов элементов управления форм FormControl, FormGroup и FormArray. Он предоставляет общие для них поведения и свойства. |
FormControl | Управляет значением и статусом проверки отдельного элемента управления формы. Соответствует элементу управления HTML-формы, например <input> или <select>. |
FormGroup | Управляет значением и состоянием проверки группы экземпляров AbstractControl. Свойства группы включают ее дочерние элементы управления. Верхнеуровневая форма в компоненте — это FormGroup. |
FormArray | Управляет значением и состоянием проверки массива экземпляров AbstractControl с числовыми индексами. |
FormBuilder | Внедряемый сервис, предоставляющий фабричные методы для создания экземпляров элементов управления. |
FormRecord | Отслеживает значение и состояние проверки коллекции экземпляров FormControl, каждый из которых имеет один и тот же тип значения. |
Директивы
| Директива | Описание |
|---|---|
FormControlDirective | Синхронизирует автономный экземпляр FormControl с элементом управления формы. |
FormControlName | Синхронизирует FormControl в существующем экземпляре FormGroup с элементом управления формы по имени. |
FormGroupDirective | Синхронизирует существующий экземпляр FormGroup с элементом DOM. |
FormGroupName | Синхронизирует вложенный экземпляр FormGroup с элементом DOM. |
FormArrayName | Синхронизирует вложенный экземпляр FormArray с элементом DOM. |
FormArrayDirective | Синхронизирует автономный экземпляр FormArray с элементом DOM. |
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/forms/reactive-forms