
Введение
Одним из достоинств Angular является широкий набор инструментов “из коробки”, которые позволяют быстро создавать формы любой сложности.
В Angular существует 2 подхода к созданию форм:
Template-driven forms — подход, в котором ключевую роль играет шаблон компонента, и все описание производится в нем — этот подход является развитием работы с формами в AngularJS;
Reactive forms — новый подход для работы с формами в реактивном стиле. Описание формы происходит в компоненте в виде дерева объектов, после чего это дерево связывается с шаблоном. Все манипуляции (проверка валидности, подписка на изменение значения и прочее) производятся в компоненте, что делает работу более гибкой, удобной и предсказуемой.
В данной статье мы разберем, как начать работать с reactive forms на примере простой формы с валидацией и сообщениями об ошибках. Код примера.
Реактивные формы. Часть 2.
Создание реактивной формы
Подключим ReactiveFormsModule в модуль, в котором будем использовать форму:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule, ReactiveFormsModule ], declarations: [ AppComponent ], bootstrap: [ AppComponent ] }) export class AppModule { }
В реактивных формах используются 3 типа блоков:
- FormControl — одиночный контрол формы;
- FormGroup — группа контролов формы;
- FormArray — массив контролов формы.
Все они наследуются от Abstract Control.
Описывать форму удобно, используя специальный инструмент FormBuilder, с помощью которого можно создавать перечисленные выше блоки.
Добавим в компонент формы FormBuilder и FormGroup:
import { Component } from '@angular/core'; import { FormGroup, FormBuilder } from '@angular/forms'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) export class AppComponent implements OnInit { myFirstReactiveForm: FormGroup; constructor(private fb: FormBuilder){} ngOnInit(){} }
Теперь опишем форму и инициализируем ее в ngOnInit:
export class AppComponent implements OnInit { myFirstReactiveForm: FormGroup; constructor(private fb: FormBuilder){} ngOnInit(){ this.initForm(); } /** Инициализация формы*/ initForm(){ this.myFirstReactiveForm = this.fb.group({ name: ['Иван'], email: [null] }); } }
Данная форма состоит из двух контролов:
- name со значением «Иван» при инициализации;
- email без стартового значения.
Свяжем форму с шаблоном компонента через директивы formGroup и formControlName:
<form [formGroup]="myFirstReactiveForm"> <label for="name">имя</label> <input type="text" id="name" formControlName="name" /> <br/><br/> <label for="email">email</label> <input type="text" id="email" formControlName="email" /> <br/><br/> <button type="submit">отправить</button> </form>
Тут нужно обратить внимание на то, что formControlName принимает имя строкой и пишется без [ ].
Данные формы мы можем получить в компоненте в виде объекта через свойство value и вывести их в шаблон через jsonPipe (на данном этапе это необходимо для проверки работоспособности):
<div> {{myFirstReactiveForm.value | json}} </div>
Валидация и подсветка не валидных контролов
Angular предоставляет возможность валидации с помощью списка статических методов класса Validators, проверяющих соответствие инпута определенным условиям.
Мы используем следующие:
- Validators.required — делает контрол обязательным для заполнения;
- Validators.email — валидация эл. адреса;
- Validators.pattern — валидация по регулярному выражению.
Импортируем валидаторы из angular/forms в компонент:
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
Добавим их в описание контролов формы:
this.myFirstReactiveForm = this.fb.group({ name: ['', [ Validators.required, Validators.pattern(/[А-я]/) ] ], email: ['', [ Validators.required, Validators.email ] ] });
На все контролы формы Angular динамически добавляет парные CSS классы в зависимости от определенных условий:
- ng-invalid/ng-valid — меняется в зависимости от валидности контрола;
- ng-pristine/ng-dirty — контрол считается dirty, если в нем хотя бы раз менялось значение;
- ng-untouched/ng-touched — контрол считается touched при первой потере фокуса.
В CSS добавим следующие стили:
input.ng-touched.ng-invalid{ border-color: red; }
Теперь при введении неверных данных и потере фокуса контролы будут иметь красный бордер.
Вывод сообщения об ошибке
Получить доступ к контролу в компоненте можно следующим образом:
this.myFirstReactiveForm.controls[controlName]
Проверим свойства invalid и touched.
Полный список свойств и методов контрола смотреть здесь.
Добавим в компонент метод для проверки валидности контрола, который принимает на вход имя контрола и возвращает true/false:
isControlInvalid(controlName: string): boolean { const control = this.myFirstReactiveForm.controls[controlName]; const result = control.invalid && control.touched; return result; }
В шаблоне добавим под контролом div с сообщением об ошибке, который будет отображаться по *ngIf, если контрол не валидный:
<label for="name">имя</label> <input type="text" id="name" formControlName="name" /> <div class="error" *ngIf="isControlInvalid('name')"> Имя должно состоять только из русских букв </div>
Для вывода разных ошибок (в зависимости от условий) можно воспользоваться библиотекой ngx-errors.
Отправка формы
Добавим в компонент метод onSubmit:
onSubmit() { const controls = this.myFirstReactiveForm.controls; /** Проверяем форму на валидность */ if (this.myFirstReactiveForm.invalid) { /** Если форма не валидна, то помечаем все контролы как touched*/ Object.keys(controls) .forEach(controlName => controls[controlName].markAsTouched()); /** Прерываем выполнение метода*/ return; } /** TODO: Обработка данных формы */ console.log(this.myFirstReactiveForm.value); }
Если форма не валидна, через foreach помечаем все контролы как touched для подсветки ошибок и прерываем выполнение метода. В противном случае обрабатываем данные формы.
Добавим обработчик события submit в шаблон:
<form [formGroup]="myFirstReactiveForm" (submit)="onSubmit()">
Форма готова!
Заключение
В следующей части разберем реактивную работу с формами, а именно:
подписку на событие изменения контрола;
динамический сброс и блокировку зависимых контролов;
динамическое добавление и удаление контролов и групп контролов в форму.
Ссылки
Код примера смотреть здесь.
Более подробную информацию можно получить из официальной документации.
Все интересующиеся Angular могут присоединяться к группе русскоговорящего Angular сообщества в Telegram.
