Spec-Zone.ru › Angular

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

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

Обзор реактивных форм

В реактивных формах используется явный и неизменяемый подход к управлению состоянием формы в определённый момент времени. Каждое изменение состояния формы возвращает новое состояние, которое сохраняет целостность модели между изменениями. Реактивные формы построены на основе потоков наблюдаемых объектов: данные полей ввода и их значения предоставляются в виде потоков, к которым можно обращаться синхронно.

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

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

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

Добавление базового элемента управления формы

Для использования элементов управления формы нужно выполнить три шага.

  1. Создайте новый компонент и зарегистрируйте модуль реактивных форм. Этот модуль объявляет директивы реактивных форм, необходимые для их использования.
  2. Создайте экземпляр FormControl.
  3. Зарегистрируйте FormControl в шаблоне.

Затем можно отобразить форму, добавив компонент в шаблон.

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

  1. Создание нового компонента и импорт 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 {
  2. Объявление экземпляра 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('');
    }
  3. Регистрация элемента управления в шаблоне

    После создания элемента управления в классе компонента необходимо связать его с элементом управления формы в шаблоне. Обновите шаблон, добавив привязку элемента управления формы с помощью formControl, предоставляемой FormControlDirective, которая также входит в ReactiveFormsModule.

    <label for="name">Name: </label>
    <input id="name" type="text" [formControl]="name" />

    Теперь с помощью синтаксиса привязки шаблона элемент управления формы зарегистрирован для элемента ввода name в шаблоне. Элемент управления формы и элемент DOM взаимодействуют друг с другом: представление отражает изменения модели, а модель — изменения представления.

  4. Отображение компонента

    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 {

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

  1. Создайте экземпляр FormGroup.
  2. Свяжите модель FormGroup с представлением.
  3. Сохраните данные формы.
  1. Создание экземпляра FormGroup

    Создайте в классе компонента свойство с именем profileForm и присвойте ему новый экземпляр группы формы. Чтобы инициализировать группу формы, передайте конструктору объект с именованными ключами, сопоставленными с соответствующими элементами управления.

    Для формы профиля добавьте два экземпляра элемента управления формы с именами firstName и lastName

    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(''),
      });
    }

    Теперь отдельные элементы управления формы объединены в группу. Экземпляр FormGroup предоставляет значение модели в виде объекта, сформированного из значений каждого элемента управления в группе. Экземпляр группы формы имеет те же свойства (например, value и untouched) и методы (например, setValue()), что и экземпляр элемента управления формы.

  2. Связывание модели 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>

    Как группа формы содержит набор элементов управления, так и FormGroup profileForm привязывается к элементу form с помощью директивы FormGroup, которая создаёт уровень взаимодействия между моделью и формой, содержащей поля ввода. Ввод formControlName, предоставляемый директивой FormControlName, связывает каждый отдельный элемент ввода с элементом управления формы, определённым в FormGroup. Элементы управления формы взаимодействуют с соответствующими элементами. Они также передают изменения экземпляру группы формы, который служит источником достоверного значения модели.

  3. Сохранение данных формы

    Компонент 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 недействительна. Пока проверка не выполняется, кнопка всегда включена. Основы проверки форм рассматриваются в разделе Проверка вводимых данных формы.

  4. Отображение компонента

    Чтобы отобразить компонент ProfileEditor, содержащий форму, добавьте его в шаблон компонента.

    <app-profile-editor />

    ProfileEditor позволяет управлять экземплярами элементов управления формы для элементов управления firstName и lastName в экземпляре группы формы.

    Создание вложенных групп форм

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

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

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

    1. Создайте вложенную группу.
    2. Сгруппируйте вложенную форму в шаблоне.

    Некоторые виды информации естественным образом объединяются в группы. Имя и адрес — типичные примеры таких вложенных групп, которые используются в следующих примерах.

    Чтобы создать вложенную группу в `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 в группе формы, к изменениям значения и состояния применяются те же правила. Изменения состояния и значения вложенной группы форм передаются родительской группе формы, сохраняя согласованность с общей моделью.

  5. Группировка вложенной формы в шаблоне

    После обновления модели в классе компонента обновите шаблон, чтобы связать экземпляр группы формы с её элементами ввода. Добавьте группу форм 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 предоставляет удобные методы для создания элементов управления.

Чтобы воспользоваться этой службой, выполните следующие действия.

  1. Импортируйте класс FormBuilder.
  2. Внедрите службу FormBuilder.
  3. Создайте содержимое формы.

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

  1. Импорт класса FormBuilder

    Импортируйте класс FormBuilder из пакета @angular/forms.

    import {FormBuilder, ReactiveFormsModule} from '@angular/forms';
  2. Внедрение службы FormBuilder

    Служба FormBuilder — это внедряемый провайдер из модуля реактивных форм. Используйте функцию inject(), чтобы внедрить эту зависимость в компонент.

    private formBuilder = inject(FormBuilder);
  3. Создание элементов управления формы

    Служба FormBuilder предоставляет три метода: control(), group() и array(). Это фабричные методы для создания экземпляров элементов управления формы, групп форм и массивов форм в классах компонентов. Используйте метод group, чтобы создать элементы управления profileForm.

    profileForm = this.formBuilder.group({
        firstName: [''],
        lastName: [''],
        address: this.formBuilder.group({
          street: [''],
          city: [''],
          state: [''],
          zip: [''],
        }),
      });

    В предыдущем примере метод group() используется с тем же объектом для определения свойств модели. Значение для каждого имени элемента управления представляет собой массив, первым элементом которого является начальное значение.

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

    profileForm = new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl(''),
        address: new FormGroup({
          street: new FormControl(''),
          city: new FormControl(''),
          state: new FormControl(''),
          zip: new FormControl(''),
        }),
      });
    profileForm = this.formBuilder.group({
        firstName: [''],
        lastName: [''],
        address: this.formBuilder.group({
          street: [''],
          city: [''],
          state: [''],
          zip: [''],
        }),
      });

Проверка вводимых данных формы

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

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

  1. Импортируйте функцию-валидатор в компонент формы.
  2. Добавьте валидатор к полю формы.
  3. Добавьте логику обработки состояния проверки.

Самая распространённая проверка — обязательность заполнения поля. В следующем примере показано, как добавить проверку обязательного заполнения для элемента управления firstName и отобразить результат проверки.

  1. Импорт функции-валидатора

    В реактивные формы входят функции-валидаторы для распространённых сценариев. Эти функции получают элемент управления, который необходимо проверить, и возвращают объект ошибки или значение null в зависимости от результата проверки.

    Импортируйте класс Validators из пакета @angular/forms.

    import {Validators} from '@angular/forms';
  2. Установка обязательного поля

    В компоненте ProfileEditor добавьте статический метод Validators.required в качестве второго элемента массива для элемента управления firstName.

    private formBuilder = inject(FormBuilder);
    
      profileForm = this.formBuilder.group({
        firstName: ['', Validators.required],
        lastName: [''],
        address: this.formBuilder.group({
          street: [''],
          city: [''],
          state: [''],
          zip: [''],
        }),
      });
  3. Отображение состояния формы

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

    Отобразите текущее состояние profileForm с помощью интерполяции.

    <p>Form Status: {{ profileForm.status }}</p>

    Кнопка Отправить отключена, потому что profileForm недействительна из-за обязательного элемента управления формы firstName. После заполнения поля ввода firstName форма станет действительной, а кнопка Отправить включится.

    Подробнее о проверке форм см. в руководстве Проверка форм.

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

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

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

  1. Импортируйте класс FormArray.
  2. Определите элемент управления FormArray.
  3. Получите доступ к элементу управления FormArray с помощью метода-геттера.
  4. Отобразите массив формы в шаблоне.

В следующем примере показано, как управлять массивом псевдонимов в ProfileEditor.

  1. Импорт класса FormArray

    Импортируйте класс FormArray из @angular/forms, чтобы использовать его для указания типов. Служба FormBuilder готова создать экземпляр FormArray.

    import {FormArray} from '@angular/forms';
  2. Определение элемента управления 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('')]),
      });

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

  3. Доступ к элементу управления 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(''));
      }

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

  4. Отображение массива формы в шаблоне

    Чтобы добавить псевдонимы из модели формы, необходимо включить их в шаблон. Подобно элементу ввода 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());
      }
    }
  5. Использование 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);
    }
  6. Добавление псевдонима

    Изначально форма содержит одно поле Alias. Чтобы добавить ещё одно поле, нажмите кнопку Добавить псевдоним. Можно также проверить массив псевдонимов, предоставленный моделью формы и отображаемый с помощью Form Value в нижней части шаблона. Вместо экземпляра элемента управления формы для каждого псевдонима можно создать другой экземпляр группы формы с дополнительными полями. Процесс определения элемента управления для каждого элемента остаётся тем же.

Унифицированные события изменения состояния элементов управления

Все элементы управления формы предоставляют единый поток событий изменения состояния элементов управления через наблюдаемый объект events класса AbstractControl (FormControl, FormGroup, FormArray и FormRecord). Этот единый поток позволяет реагировать на изменения состояния значения, статуса, чистоты, посещения и сброса, а также на действия на уровне формы, например отправку. Это позволяет обрабатывать все обновления с помощью одной подписки, вместо того чтобы настраивать несколько наблюдаемых объектов.

Типы событий

Каждый элемент, выдаваемый events, является экземпляром определённого класса события:

  • ValueChangeEvent — при изменении значения элемента управления.
  • StatusChangeEvent — при обновлении статуса проверки элемента управления до одного из значений FormControlStatus (VALID, INVALID, PENDING или DISABLED).
  • PristineChangeEvent — при изменении состояния pristine/dirty элемента управления.
  • TouchedChangeEvent — при изменении состояния touched/untouched элемента управления.
  • FormResetEvent — при сбросе элемента управления или формы через API reset() либо с помощью стандартного действия.
  • 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API