Пользовательские валидаторы форм в Angular

Валидаторы используются для обеспечения соответствия значений в форме определенным требованиям. В приложениях Angular они поддерживаются шаблонными и реактивными формами.

Читайте также: Введение в реактивные формы Angular

Существует несколько встроенных валидаторов Angular: это required, email, pattern и minLength. А для работы с функциями, которые не поддерживаются встроенными валидаторами, вы можете разработать собственные, пользовательские валидаторы.

Например, валидатор для телефонного номера – это поле ввода, которое подсвечивается красным (следовательно, не считается действительным), если указанное значение содержит менее десяти цифр. Такой валидатор мог бы выдавать предупреждение или подсказку в случае неприемлемого значения, к примеру: «Phone number must be of 10 digits».

В этом руководстве мы создадим такой пользовательский валидатор для поля ввода номера телефона в приложении Angular.

Требования

Для выполнения этого урока вам понадобится:

  • Локальная установка Node.js. Следуйте инструкциям для вашего дистрибутива: mac OSUbuntuDebianCentOS.
  • Базовое знакомство с настройкой проекта Angular.

Это руководство было проверено на версиях Node v15.2.1, npm v6.14.8, @angular/core v11.0.0 и @angular/forms v11.0.0.

Настройка проекта

В рамках этого руководства мы создадим простой проект на основе стандартного проекта Angular, созданного с помощью @angular/cli.

npx @angular/cli new angular-custom-validation-example --style=css --routing=false --skip-tests

Примечание: В качестве альтернативы вы можете установить @angular/cli глобально.

Эта команда настроит новый проект Angular со стилями CSS (также доступны Sass, Less или Stylus), без маршрутизации и тестирования.

Перейдите во только что созданный каталог проекта:

cd angular-custom-validation-example

На этом этапе у нас готов новый проект Angular.

Валидаторы в шаблонных формах

В качестве валидаторов в шаблонных формах используются директивы. В этом примере мы создадим директиву phone-number-validator с помощью @angular/cli.

Сначала откройте свой терминал и используйте пакет @angular/cli, который был установлен как зависимость разработки. Он нужен нам для создания новой директивы:

./node_modules/@angular/cli/bin/ng generate directive phone-number-validator

Эта команда создаст phone-number-validator.directive.ts и phone-number-validator.directive.spec.ts. Она также добавит PhoneNumberValidatorDirective в файл app.module.ts.

Затем откройте в редакторе кода файл phone-number-validator.directive.ts. Добавьте в него Validator, AbstractControl и NG_VALIDATORS:

import { Directive } from '@angular/core';
import { AbstractControl, Validator, NG_VALIDATORS } from '@angular/forms';

@Directive({
  selector: '[appPhoneNumberValidator]',
  providers: [{
    provide: NG_VALIDATORS,
    useExisting: PhoneNumberValidatorDirective,
    multi: true
  }]
})
export class PhoneNumberValidatorDirective implements Validator {
  validate(control: AbstractControl) : {[key: string]: any} | null {
    if (control.value && control.value.length != 10) {
      return { 'phoneNumberInvalid': true };
    }
    return null;
  }
}

Этот код создает директиву, которая реализует Validator для @angular/forms. Для этой реализации потребуется следующий метод:

validate(control: AbstractControl): : {[key: string]: any} | null

Этот валидатор вернет объект – { ‘phoneNumberInvalid’: true }  – если значение не соответствует заданному условию (то есть, если оно включает в себя менее 10 символов). В противном случае, если значение соответствует условию, валидатор вернет null.

Затем откройте свой терминал и используйте пакет @angular/cli, который был установлен как зависимость разработки, для создания новой директивы:

./node_modules/@angular/cli/bin/ng generate component template-driven-form-example --flat

Эта команда создаст файлы template-driven-form-example.component.ts и template-driven-form-example.component.html . Она также добавит TemplateDrivenFormExampleComponent в файл app.module.ts.

Затем откройте template-driven-form-example.component.ts в редакторе кода и добавьте phone с пустой строкой в качестве начального значения.

import { Component } from '@angular/core';

@Component({
  selector: 'app-template-driven-form-example',
  templateUrl: './template-driven-form-example.component.html',
  styleUrls: ['./template-driven-form-example.component.css']
})
export class TemplateDrivenFormExampleComponent {
  phone = '';
}

Angular добавляет возвращаемое значение валидатора в свойство errors в FormControl / NgModel. Если это свойство не пусто, форма считается недействительной. Если же оно пусто, значит форма проходит проверку.

Чтобы использовать директиву в шаблонной форме, откройте простую шаблонную форму example.component.html и добавьте следующий код:

<div class="form-group">
  <label>Phone
    <input
      type="text"
      class="form-control"
      name="phone"
      [(ngModel)]="phone"
      #phoneNgModel="ngModel"
      appPhoneNumberValidator
      [class.is-invalid]="(phoneNgModel.touched || phoneNgModel.dirty) && phoneNgModel.errors?.phoneNumberInvalid"
    >
  </label>
  <span
    class="invalid-feedback"
    *ngIf="(phoneNgModel.touched || phoneNgModel.dirty) && phoneNgModel.errors?.phoneNumberInvalid"
    >
      Phone number must be 10 digits
  </span>
</div>

Этот код создает элемент <input> и <span> с сообщением об ошибке. Элемент <input> использует ngModel и селектор appPhoneNumberValidator для директивы.

Если <input> получил значение touched или dirty и форма не проходит проверку, произойдут две вещи. Во-первых, к <input> будет применен класс is-invalid. Во-вторых, на экране отобразится <span> с сообщением об ошибке.

Примечание: Некоторые из этих классов – form-group, form-control, invalid-feedback и is-valid – являются частью Bootstrap Framework. Они не нужны для выполнения данного руководства, но могут придать форме визуальную выразительность.

Затем откройте app.module.ts в редакторе кода и добавьте FormModule:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { PhoneNumberValidatorDirective } from './phone-number-validator.directive';
import { TemplateDrivenFormExampleComponent } from './template-driven-form-example.component';

@NgModule({
  declarations: [
    AppComponent
    PhoneNumberValidatorDirective,
    TemplateDrivenFormExampleComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

А теперь откройте app.component.html и замените его контент своим TemplateDrivenFormExample:

<app-template-driven-form-example></app-template-driven-form-example>

Вы можете запустить команду npm start и попробовать ввести данные в веб-браузере. Если вы введете меньше или больше 10 символов в поле для телефона, на экране отобразится сообщение об ошибке.

На этом этапе в вашей шаблонной форме есть пользовательский валидатор, использующий директиву.

Валидаторы в реактивных формах

Вместо директив реактивные формы используют в качестве валидаторов функции.

Сначала откройте свой терминал и используйте пакет @angular/cli, который был установлен как зависимость разработки:

./node_modules/@angular/cli/bin/ng generate component reactive-form-example --flat

Эта команда создаст файлы reactive-form-example.component.ts и reactive-form-example.component.html, а также добавит ReactiveFormExampleComponent в app.module.ts.

Затем откройте reactive-form-example.component.ts в редакторе кода и добавьте FormBuilder и AbstractControl:

import { Component, OnInit } from "@angular/core";
import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-reactive-form-example',
  templateUrl: './reactive-form-example.component.html',
  styleUrls: ['./reactive-form-example.component.css']
})
export class ReactiveFormExampleComponent implements OnInit {
  myForm: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.myForm = this.fb.group({
      phone: ['', [ValidatePhone]]
    });
  }

  saveForm(form: FormGroup) {
    console.log('Valid?', form.valid); // true or false
    console.log('Phone Number', form.value.phone);
  }
}

function ValidatePhone(control: AbstractControl): {[key: string]: any} | null  {
  if (control.value && control.value.length != 10) {
    return { 'phoneNumberInvalid': true };
  }
  return null;
}

Этот код создает функцию ValidatePhone и добавляет ее в массив валидаторов FormControl.

Откройте reactive-form-example.component.html в редакторе кода и создайте следующую форму:

<form
  class="needs-validation"
  novalidate
  [formGroup]="myForm"
  (ngSubmit)="saveForm(myForm)"
> 
  <div class="row">
    <div class="form-group col-sm-4">
      <label>
        Phone
        <input
          type="text"
          class="form-control"
          formControlName="phone"
          [class.is-invalid]="(myForm.get('phone').touched || myForm.get('phone').dirty) && myForm.get('phone').invalid"
        >
      </label>
      <span
        class="invalid-feedback"
        *ngIf="(myForm.get('phone').touched || myForm.get('phone').dirty) && myForm.get('phone').invalid"
      >
        Phone number must be 10 digits
      </span>
    </div>
  </div>
</form>

В отличие от шаблонной формы, реактивная форма поддерживает form и использует [formGroup], (ngSubmit), formControlName и get.

Затем откройте app.module.ts в редакторе кода и добавьте ReactiveFormsModule:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { PhoneNumberValidatorDirective } from './phone-number-validator.directive';
import { ReactiveFormExampleComponent } from './reactive-form-example.component';
import { TemplateDrivenFormExampleComponent } from './template-driven-form-example.component';

@NgModule({
  declarations: [
    AppComponent,
    PhoneNumberValidatorDirective,
    ReactiveFormExampleComponent,
    TemplateDrivenFormExampleComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Наконец, откройте app.component.html и замените текущий контент своим ReactiveFormExample:

<app-reactive-form-example></app-reactive-form-example>

Вы можете запустить команду npm start и попробовать ввести данные в веб-браузере. Если вы введете меньше или больше 10 символов в поле для телефона, на экране отобразится сообщение об ошибке.

На этом этапе в вашей реактивной форме есть пользовательский валидатор, использующий функцию.

Заключение

В этой статье вы научились добавлять пользовательские валидаторы для разных видов форм в приложении Angular.

Пользовательские валидаторы гарантируют, что значения, которые вводят посетители вашего приложения, соответствуют вашим ожиданиям.

Для более глубокого понимания концепций из этого руководства рекомендуем почитать больше об AbstractControl.

Tags: , ,

Добавить комментарий