Типизированные формы¶
10.05.2022
Начиная с Angular 14, реактивные формы по умолчанию строго типизированы.
Предварительные условия¶
В качестве основы для этого руководства вы должны быть знакомы с Angular Reactive Forms.
Обзор типизированных форм¶
В реактивных формах Angular вы явно указываете модель формы. В качестве простого примера рассмотрим базовую форму входа пользователя в систему:
1 2 3 4 |
|
Angular предоставляет множество API для взаимодействия с этой FormGroup
. Например, вы можете вызвать login.value
, login.controls
, login.patchValue
и т. д. (Полное описание API см. в документации API).
В предыдущих версиях Angular большинство этих API включали any
в свои типы, и взаимодействие со структурой элементов управления или самих значений не было безопасным с точки зрения типов. Например, вы могли написать следующий некорректный код:
1 |
|
При использовании строго типизированных реактивных форм приведенный выше код не компилируется, поскольку у email
нет свойства domain
.
В дополнение к дополнительной безопасности, типы позволяют получить ряд других улучшений, таких как улучшенное автозаполнение в IDE и явный способ задания структуры формы.
В настоящее время эти улучшения применяются только к реактивным формам (не к шаблонным формам).
Автоматическая миграция нетипизированных форм¶
При обновлении до Angular 14 включенная миграция автоматически заменит все классы форм в вашем коде на соответствующие нетипизированные версии. Например, фрагмент, приведенный выше, станет:
1 2 3 4 |
|
Каждый символ Untyped
имеет точно такую же семантику, как и в предыдущих версиях Angular, поэтому ваше приложение должно компилироваться как и раньше. Удалив префиксы Untyped
, вы можете постепенно включать типы.
FormControl
: Начало работы¶
Самая простая форма состоит из одного элемента управления:
1 |
|
Этот элемент управления будет автоматически считаться имеющим тип FormControl<string|null>
. TypeScript будет автоматически применять этот тип во всем API FormControl
, например, email.value
, email.valueChanges
, email.setValue(...)
и т. д.
Nullability¶
Вы можете задаться вопросом: почему тип этого элемента управления включает null
? Это потому, что элемент управления может стать null
в любое время, вызвав команду reset:
1 2 3 |
|
TypeScript требует, чтобы вы всегда обрабатывали возможность того, что элемент управления стал null
. Если вы хотите сделать этот элемент управления ненулевым, вы можете использовать опцию nonNullable
. Это приведет к тому, что элемент управления сбросится на свое начальное значение, а не на null
:
1 2 3 4 5 |
|
Повторимся, что этот параметр влияет на поведение вашей формы во время выполнения, когда вызывается .reset()
, и его следует использовать с осторожностью.
Указание явного типа¶
Можно указать тип, вместо того чтобы полагаться на умозаключение. Рассмотрим элемент управления, который инициализируется значением null
. Поскольку начальное значение равно null
, TypeScript выведет FormControl<null>
, что является более узким, чем мы хотим.
1 2 |
|
Чтобы предотвратить это, мы явно указываем тип как string|null
:
1 2 |
|
FormArray
: Динамические, однородные коллекции¶
Массив FormArray
содержит неограниченный список элементов управления. Параметр type
соответствует типу каждого внутреннего элемента управления:
1 2 |
|
Этот FormArray
будет иметь внутренний элемент управления типа FormControl<string|null>
.
Если вы хотите иметь несколько различных типов элементов внутри массива, вы должны использовать UntypedFormArray
, потому что TypeScript не может определить, какой тип элемента будет встречаться в той или иной позиции.
FormGroup
и FormRecord
¶
Angular предоставляет тип FormGroup
для форм с перечислимым набором ключей и тип FormRecord
для открытых или динамических групп.
Частичные значения¶
Рассмотрим еще раз форму входа в систему:
1 2 3 4 |
|
В любой FormGroup
можно отключить элементы управления. Любой отключенный элемент управления не будет отображаться в значении группы.
Как следствие, тип login.value
— Partial<{email: string, password: string}>
. Partial
в этом типе означает, что каждый член может быть неопределенным.
Более конкретно, тип login.value.email
— string|undefined
, и TypeScript будет принудительно обрабатывать возможно неопределенное
значение (если у вас включена strictNullChecks
).
Если вы хотите получить доступ к значению включая отключенные элементы управления, и таким образом обойти возможные неопределенные
поля, вы можете использовать login.getRawValue()
.
Дополнительные элементы управления и динамические группы¶
В некоторых формах есть элементы управления, которые могут присутствовать или не присутствовать, которые могут быть добавлены и удалены во время выполнения. Вы можете представить эти элементы управления с помощью опциональных полей:
1 2 3 4 5 6 7 8 9 10 11 |
|
В этой форме мы явно указываем тип, что позволяет нам сделать элемент управления password
необязательным. TypeScript будет следить за тем, чтобы можно было добавлять или удалять только необязательные элементы управления.
FormRecord
¶
Некоторые случаи использования FormGroup
не соответствуют приведенному выше шаблону, поскольку ключи не известны заранее. Класс FormRecord
предназначен для этого случая:
1 2 3 4 5 6 7 |
|
Любой элемент управления типа string|null
может быть добавлен к этой FormRecord
.
Если вам нужна FormGroup
, которая является одновременно динамической (открытой) и неоднородной (элементы управления разных типов), то улучшенная безопасность типов невозможна, и вам следует использовать UntypedFormGroup
.
Запись FormRecord
также может быть построена с помощью FormBuilder
:
1 |
|
FormBuilder
и NonNullableFormBuilder
¶
Класс FormBuilder
был модернизирован для поддержки новых типов, аналогично приведенным выше примерам.
Кроме того, доступен дополнительный конструктор: NonNullableFormBuilder
. Этот тип является сокращением для указания {nonNullable: true}
на каждом элементе управления, и может устранить значительную часть шаблонов из больших форм с недействительными элементами. Вы можете получить к нему доступ, используя свойство nonNullable
на FormBuilder
:
1 2 3 4 5 |
|
В приведенном выше примере оба внутренних элемента управления будут non-nullable (т.е. будет установлено значение nonNullable
).
Вы также можете внедрить его, используя имя NonNullableFormBuilder
.