Я был удивлен, когда узнал что в Angular нет кастомизации ошибок при валидации формы. То есть, встроенные билдеры форм есть, встроенные валидаторы есть, но вот встроенных текстов ошибок нет. Соответственно менять то нечего. Нужно создавать свои. А чтобы каждый раз не писать захардкоженый вывод ошибок, можно создать шаблон, в котором будет меняться только текст ошибок.
Создание формы
Создадим стандартную форму в Angular
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Import { FormGroup, FormBuilder } from '@angular/forms'; export class MyFormPage { myForm: FormGroup; constructor( private fb: FormBuilder, ) { } private createForm(): void { this.myForm = this.fb.group({ name: ['', [Validators.required]], email: ['', [Validators.required, Validators.email]], phone: ['', Validators.minLength(7)], }); } } |
Что мы имеем?
3 поля с разными именами
3 разных валидатора- обязательное поле, емаил, валидация длины строки. Для каждого валидатора нужно понятное сообщение для показа пользователю.
Что предлагает Angular? Пример из официальной документации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger"> <div *ngIf="name.errors.required"> Name is required. </div> <div *ngIf="name.errors.minlength"> Name must be at least 4 characters long. </div> <div *ngIf="name.errors.forbiddenName"> Name cannot be Bob. </div> </div> |
Расписывать каждый тип ошибки? На одно только поле их набралось три. А если это сложная форма регистрации с несколькими полями? Дублировать одно и то же. Ужас.
Другой путь
Напишем свое решение. Для начала нужно понять какое поле невалидное. Получить список полей не прошедших валидацию можно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private getFormErrors(): Array<any> { let result = []; Object.keys(this.myForm.controls).forEach(key => { const controlError: ValidationErrors = this.myForm.get(key).errors; if (controlError) { Object.keys(controlError).forEach(keyError => { result.push({ 'control': key, 'error': keyError, 'value': controlError[keyError], }); }); } }); return result; } |
После получения списка ошибок, нужно придумать для каждого текст. Воспользуемся наработками из Laravel and Yii2. Там для каждого поля в коде можно прописать свой текст.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
errorMessages = { "required": "Введите {label}", "email": "Email должен быть в формате myMail@almat.su", "minlength": "Текущая длина {actualLength}. Минимальная длина должна быть {requiredLength} и более" }; formLabels = { "name": "Имя", "email": "Емаил", "phone": "Телефон", }; |
errorMessages — состоит из тип_ошибки: текст_ошибки
formLabels — чисто опционально, чтобы вместо сухого «Обязательное поле», «Введите значение в поле», было «Введите Имя» или «Введите Телефон»
Делаем подстановку сообщении:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
private getFormErrors(): Array<Object> { let result = []; ..................... Object.keys(controlError).forEach(errorType => { let message = this.formatMessage(fieldName, errorType, controlError); result.push({ ....................... 'value': controlError[errorType], 'message': message }); }); } }); return result; } formatMessage(fieldName: string, errorType: string, controlError: ValidationErrors): string { let message: string; if (errorType == "minlength") { message = this.errorMessages[errorType] .replace(/{actualLength}/, controlError[errorType].actualLength) .replace(/{requiredLength}/, controlError[errorType].requiredLength); console.log(message); } else { message = this.errorMessages[errorType].replace(/{label}/, this.formLabels[fieldName]) } return message; } |
Обработчик при отправке формы:
1 2 3 4 5 6 7 8 9 10 11 12 |
myAwesomeSubmitHanlder(): void { if (!this.myForm.valid) { let errors = this.getFormErrors(); let toast = this.toastCtl.create({ message: errors[0].message, }); toast.present(); } } |
Получился вот такой вывод: