Валидация форм является неотъемлемой частью веб-приложений, позволяющей обеспечить правильность и полноту данных, введённых пользователем. В Angular валидация форм может быть реализована как с помощью реактивных форм (Reactive Forms), так и с использованием шаблонных форм (Template-Driven Forms). Каждый из этих подходов имеет свои особенности и может быть применён в зависимости от требований к проекту.
Шаблонные формы — это более простая и декларативная форма, которая
подходит для небольших проектов или если необходимо быстро создать
интерфейс без сложных бизнес-логик. В этом случае Angular связывает
модель данных с представлением через директивы в шаблоне, такие как
ngModel, required, minlength и
другие.
Пример базовой шаблонной формы с валидацией:
<form #form="ngForm" (ngSubmit)="onSubmit(form)">
<div>
<label for="username">Имя пользователя</label>
<input id="username" name="username" ngModel required minlength="5">
<div *ngIf="form.controls['username']?.invalid && form.controls['username']?.touched">
<small *ngIf="form.controls['username']?.errors?.['required']">Поле обязательно для заполнения.</small>
<small *ngIf="form.controls['username']?.errors?.['minlength']">Имя должно быть не менее 5 символов.</small>
</div>
</div>
<button type="submit" [disabled]="form.invalid">Отправить</button>
</form>
В этом примере форма проверяет поле на обязательность
(required) и минимальную длину (minlength).
Валидация выполняется на основе встроенных директив, и ошибки
отображаются непосредственно в шаблоне.
Реактивные формы — это более гибкий и мощный подход, при котором
валидация и управление состоянием формы выполняются на уровне
компонента. Реактивные формы требуют явного создания и управления
формами в классе компонента с использованием FormGroup,
FormControl и FormBuilder.
Пример базовой реактивной формы с валидацией:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-registration',
templateUrl: './registration.component.html',
})
export class RegistrationComponent {
registrationForm: FormGroup;
constructor(private fb: FormBuilder) {
this.registrationForm = this.fb.group({
username: ['', [Validators.required, Validators.minLength(5)]],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
}
onSubmit(): void {
if (this.registrationForm.valid) {
console.log('Форма отправлена', this.registrationForm.value);
}
}
}
В шаблоне форма выглядит следующим образом:
<form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
<div>
<label for="username">Имя пользователя</label>
<input id="username" formControlName="username">
<div *ngIf="registrationForm.controls['username'].invalid && registrationForm.controls['username'].touched">
<small *ngIf="registrationForm.controls['username'].errors?.['required']">Поле обязательно для заполнения.</small>
<small *ngIf="registrationForm.controls['username'].errors?.['minlength']">Имя должно быть не менее 5 символов.</small>
</div>
</div>
<div>
<label for="email">Электронная почта</label>
<input id="email" formControlName="email">
<div *ngIf="registrationForm.controls['email'].invalid && registrationForm.controls['email'].touched">
<small *ngIf="registrationForm.controls['email'].errors?.['required']">Поле обязательно для заполнения.</small>
<small *ngIf="registrationForm.controls['email'].errors?.['email']">Неверный формат электронной почты.</small>
</div>
</div>
<div>
<label for="password">Пароль</label>
<input id="password" formControlName="password" type="password">
<div *ngIf="registrationForm.controls['password'].invalid && registrationForm.controls['password'].touched">
<small *ngIf="registrationForm.controls['password'].errors?.['required']">Поле обязательно для заполнения.</small>
<small *ngIf="registrationForm.controls['password'].errors?.['minlength']">Пароль должен быть не менее 8 символов.</small>
</div>
</div>
<button type="submit" [disabled]="registrationForm.invalid">Отправить</button>
</form>
Реактивные формы позволяют значительно проще управлять состоянием формы, а также легче работать с динамическими формами и сложными валидациями.
В Angular доступны различные типы валидаций:
Встроенные валидаторы:
required — проверка на обязательность заполнения
поля.minlength и maxlength — проверка
минимальной и максимальной длины.email — проверка на корректность формата электронной
почты.pattern — проверка с использованием регулярных
выражений.Эти валидаторы могут быть использованы как в шаблонных, так и в реактивных формах.
Пользовательские валидаторы: Для создания пользовательских валидаторов необходимо определить функцию, которая будет возвращать объект с ошибками в случае невалидных данных.
Пример пользовательского валидатора:
import { AbstractControl, ValidationErrors } from '@angular/forms';
export function usernameValidator(control: AbstractControl): ValidationErrors | null {
const forbidden = /admin/i.test(control.value);
return forbidden ? { forbiddenName: { value: control.value } } : null;
}
Этот валидатор проверяет, не является ли введённое имя пользователя словом “admin” (нечувствительно к регистру).
Асинхронные валидаторы полезны, когда необходимо выполнить проверку
данных, требующую обращения к серверу, например, проверка уникальности
имени пользователя. В отличие от синхронных валидаторов, асинхронные
валидаторы возвращают Observable или
Promise.
Пример асинхронного валидатора:
import { AbstractControl, ValidationErrors } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { debounceTime, map, switchMap } from 'rxjs/operators';
export function usernameAsyncValidator(control: AbstractControl): Observable<ValidationErrors | null> {
return of(control.value).pipe(
debounceTime(300),
switchMap(username => {
return checkUsernameAvailability(username).pipe(
map(isAvailable => isAvailable ? null : { usernameTaken: { value: control.value } })
);
})
);
}
function checkUsernameAvailability(username: string): Observable<boolean> {
// Функция имитирует проверку доступности имени пользователя на сервере.
return of(username !== 'admin');
}
Этот валидатор проверяет, доступно ли имя пользователя на сервере, и
если оно занято, возвращает ошибку usernameTaken.
Angular позволяет легко отслеживать состояние формы с помощью
свойств, таких как valid, invalid,
pristine, dirty, touched и
untouched.
valid / invalid:
указывает, прошла ли форма или контрол валидацию.pristine / dirty:
отражает, было ли изменено поле.touched / untouched:
указывает, было ли поле посещено пользователем.Пример использования состояния формы:
<div *ngIf="registrationForm.invalid && registrationForm.touched">
<p>Форма содержит ошибки!</p>
</div>
Можно применить валидацию не только к отдельным полям, но и к целым группам или всей форме. Это позволяет реализовывать более сложные проверки, такие как проверка на совпадение пароля и подтверждения пароля.
Пример валидации на уровне группы:
this.registrationForm = this.fb.group({
password: ['', [Validators.required, Validators.minLength(8)]],
confirmPassword: ['', [Validators.required]]
}, { validators: this.passwordMatchValidator });
Валидация пароля и подтверждения пароля:
passwordMatchValidator(group: FormGroup): ValidationErrors | null {
const password = group.get('password')?.value;
const confirmPassword = group.get('confirmPassword')?.value;
return password === confirmPassword ? null : { passwordMismatch: true };
}
Angular позволяет легко локализовать сообщения об ошибках, используя сервисы и механизмы интернационализации (i18n). Для этого можно создать словарь ошибок и привязать его к элементам