[Из песочницы] Реактивные формы (reactive forms) Angular 5 (2+) Часть 1
Введение
Одним из достоинств Angular является широкий набор инструментов «из коробки», которые позволяют быстро создавать формы любой сложности.
В Angular существует 2 подхода к созданию форм:
Template-driven forms — подход, в котором ключевую роль играет шаблон компонента, и все описание производится в нем — этот подход является развитием работы с формами в AngularJS;
Reactive forms — новый подход для работы с формами в реактивном стиле. Описание формы происходит в компоненте в виде дерева объектов, после чего это дерево связывается с шаблоном. Все манипуляции (проверка валидности, подписка на изменение значения и прочее) производятся в компоненте, что делает работу более гибкой, удобной и предсказуемой.
В данной статье мы разберем, как начать работать с reactive forms на примере простой формы с валидацией и сообщениями об ошибках. Код примера.
Создание реактивной формы
Подключим 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:
Тут нужно обратить внимание на то, что formControlName принимает имя строкой и пишется без [ ].
Данные формы мы можем получить в компоненте в виде объекта через свойство value и вывести их в шаблон через jsonPipe (на данном этапе это необходимо для проверки работоспособности):
{{myFirstReactiveForm.value | json}}
Валидация и подсветка не валидных контролов
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, если контрол не валидный:
Имя должно состоять только из русских букв
Для вывода разных ошибок (в зависимости от условий) можно воспользоваться библиотекой 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 в шаблон:
Форма готова!
Заключение
В следующей части разберем реактивную работу с формами, а именно:
подписку на событие изменения контрола;
динамический сброс и блокировку зависимых контролов;
динамическое добавление и удаление контролов и групп контролов в форму.
Ссылки
Код примера смотреть здесь.
Более подробную информацию можно получить из официальной документации.
Все интересующиеся Angular могут присоединяться к группе русскоговорящего Angular сообщества в Telegram.