Валидация формы — важная часть любого веб-приложения. В Angular поддерживаются как встроенные валидаторы, так и возможность создания собственных, пользовательских. Пользовательские валидаторы позволяют задать специфические условия для проверки данных, которые не могут быть покрыты стандартными средствами фреймворка.
Пользовательский валидатор в Angular представляет собой функцию,
которая принимает объект AbstractControl и возвращает либо
объект с ошибками, либо null, если данные валидны. Этот
валидатор можно использовать как в реактивных, так и в шаблонных
формах.
Пример структуры валидатора:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function customValidator(control: AbstractControl): ValidationErrors | null {
// Логика проверки
if (control.value && control.value.length < 5) {
return { 'customError': { message: 'Значение слишком короткое' } };
}
return null; // Возвращается null, если значение валидно
}
AbstractControl — это абстракция, которая предоставляет
доступ к значениям и состоянию формы или поля.ValidationErrors — объект ошибок, где ключами являются
названия ошибок, а значениями — данные ошибки.Если валидатор возвращает null, это означает, что
валидация прошла успешно. В противном случае возвращается объект с
ошибками.
Для использования пользовательского валидатора в реактивных формах,
его необходимо передать в настройку поля формы с помощью метода
setValidators. В отличие от шаблонных форм, где валидаторы
добавляются прямо в HTML, в реактивных формах валидаторы добавляются
программно.
Пример использования валидатора в реактивной форме:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { customValidator } from './custom-validator';
@Component({
selector: 'app-custom-form',
templateUrl: './custom-form.component.html'
})
export class CustomFormComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
username: ['', [Validators.required, customValidator]]
});
}
onSubmit() {
if (this.form.valid) {
console.log('Форма отправлена');
} else {
console.log('Ошибка валидации');
}
}
}
В данном примере поле username валидируется с помощью
стандартного валидатора Validators.required и
пользовательского валидатора customValidator.
В шаблонных формах валидаторы применяются через атрибуты в
HTML-разметке. Для этого пользовательский валидатор можно подключить
через директиву ngModel и задать его в списке
валидаторов.
Пример использования валидатора в шаблонной форме:
<form #form="ngForm">
<input name="username" ngModel [ngModelOptions]="{standalone: true}" [ngModel]="username" required appCustomValidator>
<div *ngIf="form.controls['username']?.errors?.customError">
{{ form.controls['username'].errors.customError.message }}
</div>
</form>
В этом примере валидатор будет связан с элементом через кастомную
директиву. К тому же, ошибки выводятся с использованием стандартной
директивы ngIf для контроля состояния ошибок.
Ошибки, возвращаемые пользователем в валидаторе, можно обрабатывать и показывать пользователю, добавляя условия в шаблоне или в компоненте. Например, в компоненте можно получить доступ к ошибкам через свойства формы:
if (this.form.controls['username'].hasError('customError')) {
console.log(this.form.controls['username'].errors['customError'].message);
}
Этот код проверяет наличие ошибки customError и выводит
сообщение об ошибке, если оно существует.
В некоторых случаях необходимо проверять данные асинхронно, например,
при запросах к серверу для проверки уникальности значения. В Angular для
этого предусмотрены асинхронные валидаторы, которые возвращают
Observable вместо обычного объекта ошибки или
null.
Пример асинхронного валидатора:
import { AbstractControl, ValidationErrors, AsyncValidatorFn } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { debounceTime, switchMap, map, catchError } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
export function uniqueUsernameValidator(http: HttpClient): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
return of(control.value).pipe(
debounceTime(300),
switchMap(value =>
http.get(`https://api.example.com/check-username?username=${value}`).pipe(
map((response: any) => (response.isAvailable ? null : { 'usernameTaken': true })),
catchError(() => of(null))
)
)
);
};
}
В данном примере проверяется, доступен ли выбранный пользователем
логин через запрос к серверу. Если логин занят, возвращается ошибка с
ключом usernameTaken.
Асинхронные валидаторы применяются в форме так же, как и обычные:
this.form = this.fb.group({
username: ['', [Validators.required], [uniqueUsernameValidator(this.http)]]
});
Angular позволяет комбинировать несколько валидаторов — как синхронных, так и асинхронных. В случае, если несколько валидаторов возвращают ошибку, все ошибки сохраняются в объекте ошибок, и все сообщения ошибок могут быть отображены в интерфейсе.
this.form = this.fb.group({
email: [
'',
[Validators.required, Validators.email],
[this.emailAsyncValidator]
]
});
Здесь поле email проверяется как на обязательность заполнения, так и
на корректность email-формата с помощью встроенного валидатора
Validators.email. Параллельно применяется асинхронная
проверка на уникальность email через
emailAsyncValidator.
updateValueAndValidity() на поле формы.Использование пользовательских валидаторов позволяет гибко настроить валидацию форм, обеспечивая их соответствие бизнес-логике и требованиям проекта.