Типизированные формы¶
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.