Типизированные формы в Angular
В данной статье рассмотрим типизацию форм, которая появилась в 14 версии Angular.
В статье приведены комментарии и примеры к официальной документации Angular.
После нового релиза фреймворка, появилась отличная возможность у форм — указывать явные типы в формах. Подробнее в документации — typed forms.
Существенное отличие от предыдущих версий это добавление новых типов и добавление строгой типизации для ряда интерфейсов.
UntypedFormGroup
, UntypedFormControl
, UntypedFormArray
все эти типы пришли на замену базовым интерфейсам FormGroup
, FormControl
и FormArray
.
Формально это сокращения над интерфейсами, где убрана строгая типизация:
type UntypedFormGroup = FormGroup<any>;
type UntypedFormControl = FormControl<any>;
type UntypedFormArray = FormArray<any>;
Из новых типов это FormRecord
который служит динамическим полем в FormGroup.
const addresses = new FormRecord<FormControl<string|null>>({});addresses.addControl('Andrew', new FormControl('2340 Folsom St'));
Различия типизированных и не типизированных форм
Основные различия типизированных и нетипизированных форм в проверке типов.
Создадим обычную форму:
const form = new FormGroup({
email: new FormControl(''),
password: new FormControl(''),
});
Так как в
FormGroup
по умолчанию типany
, то результат будет аналогичен формам Angular до 14 версии.
Если мы хотим продолжить использовать формы в не типизированном варианте, то следует использовать untyped
реализации:
const form = new UntypedFormGroup({
email: new UntypedFormControl(''),
password: new UntypedFormControl(''),
});
Если получить значение формы form.value
— то тип возвращаемого значения будет any
.
// type formValue - any
const formValue = form.value;
Для того, чтобы форма возвращала конкретные типы нужно указывать типизацию формы. Для этого создадим новый интерфейс формы:
interface LoginForm {
email: FormControl<string>;
password: FormControl<string>;
}
и используем его при создании:
const form = new FormGroup<LoginForm>({
email: new FormControl('', {nonNullable: true}),
password: new FormControl('', {nonNullable: true}),
});
Теперь если получить значение формы, то форма вернет Partial от всех полей:
const formValue = form.value;
Тип formValue
будет Partial<{ email: string; password: string}>
.
Это связано с тем, что disabled контролы не участвуют в отдаче типа.
Для того, чтобы получить полный список полей нужно использовать getRawValue
.
const formValue = form.getRawValue();
В данном случае formValue
будет { email: string; password: string}
.
Если есть валидаторы:
form = new FormGroup<LoginForm>({
email: new FormControl('', {
nonNullable: true,
validators: [Validators.required, Validators.email]
}),
password: new FormControl('', {
nonNullable: true,
validators: [Validators.required, Validators.minLength(6)]
}),
});
То для того чтобы получить корректное значение формы нужно еще проверить валидность формы.
if (form.valid) {
const formValue = form.getRawValue();
}
Оптимизация работы с типизированными формами
Единственное что начинает раздражать при работе с типизированными формами — это создание интерфейсов для форм.
// Данные которые будут передаваться в API
interface Login {
email: string;
password: string;
}// Интерфейс для формы данных
interface LoginForm {
email: FormControl<string>;
password: FormControl<string>;
}
Этот процесс можно упростить, если добавить тип, который будет приводить интерфейс к интерфейсу формы:
Почему Angular не создали свой тип для упрощения я могу лишь гадать.
Отмечу, что выше приведенный интерфейс не обрабатывает корректно массивы, которые по видимому должны быть FormArray.
Тогда вышеприведенный пример можно сократить, заменив LoginForm
на Form<Login>
:
const form = new FormGroup<Form<Login>>({
email: new FormControl('', {nonNullable: true}),
password: new FormControl('', {nonNullable: true}),
});
И соответственно получить значение формы оно будет совпадать с указанным интерфейсом:
const formValue: Login = form.getRawValue();
Резюме
В Angular 14 добавили проверку типов. Если прочитать документацию пару раз, то можно разобраться в новом механизме типизации форм.
Ключевые различия:
- Для продолжения использования не типизированных форм необходимо использовать Untyped типы.
- Для того, чтобы получать полные типы (не partial) нужно использовать
getRawValue()
. - Для уменьшения количества создаваемых интерфейсов необходимо добавить в проект тип приведения интерфейсов к интерфейсу формы.
Ссылки
Подписывайтесь на блог, чтобы не пропустить новые статьи про Angular, и веб-разработку. Medium | Telegram| VK |Tw| Ln