javascript

Реактивные формы (reactive forms) Angular 5 (2+) Часть 1

  • вторник, 9 января 2018 г. в 03:12:39
https://habrahabr.ru/post/346242/
  • Разработка веб-сайтов
  • JavaScript
  • Angular




Введение


Одним из достоинств 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:

<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.