Типизированные формы в Angular

Aleksandr Serenko
F.A.F.N.U.R
Published in
3 min readAug 12, 2022
Типизированные формы в 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

--

--

Aleksandr Serenko
F.A.F.N.U.R

Senior Front-end Developer, Angular evangelist, Nx apologist, NodeJS warlock