Шаг 1: Валидация информации о кредите
Валидация полей кредита с правилами required, min/max и условными правилами.
Что мы валидируем
Шаг 1 содержит поля, связанные с кредитом, которые нуждаются в валидации:
| Поле | Правила валидации |
|---|---|
loanType | Обязательно |
loanAmount | Обязательно, min 50 000, max 10 000 000 |
loanTerm | Обязательно, min 6 месяцев, max 360 месяцев |
loanPurpose | Обязательно, minLength 10, maxLength 500 |
propertyValue | Обязательно когда loanType = 'mortgage', min 1 000 000 |
initialPayment | Обязательно когда loanType = 'mortgage' |
carBrand | Обязательно когда loanType = 'car' |
carModel | Обязательно когда loanType = 'car' |
carYear | Обязательно когда loanType = 'car', min 2000 |
carPrice | Обязательно когда loanType = 'car' |
Создание файла валидатора
Создайте файл валидатора для Шага 1:
mkdir -p src/schemas/validators
touch src/schemas/validators/loan-info.ts
Реализация
Валидация обязательных полей
Начните с базовых обязательных полей и числовых диапазонов:
src/schemas/validators/loan-info.ts
import { required, min, max, minLength, maxLength, applyWhen } from '@reformer/core/validators';
import type { ValidationSchemaFn, FieldPath } from '@reformer/core';
import type { CreditApplicationForm } from '@/types';
/**
* Валидация для Шага 1: Информация о кредите
*
* Валидирует:
* - Обязательные поля (loanType, loanAmount, loanTerm, loanPurpose)
* - Числовые диапазоны (сумма, срок)
* - Условные поля ипотеки
* - Условные поля автокредита
*/
export const loanValidation: ValidationSchemaFn<CreditApplicationForm> = (
path: FieldPath<CreditApplicationForm>
) => {
// ==========================================
// Тип кредита
// ==========================================
required(path.loanType, { message: 'Пожалуйста, выберите тип кредита' });
// ==========================================
// Сумма кредита
// ==========================================
required(path.loanAmount, { message: 'Сумма кредита обязательна' });
min(path.loanAmount, 50000, { message: 'Минимальная сумма: 50 000' });
max(path.loanAmount, 10000000, { message: 'Максимальная сумма: 10 000 000' });
// ==========================================
// Срок кредита
// ==========================================
required(path.loanTerm, { message: 'Срок кредита обязателен' });
min(path.loanTerm, 6, { message: 'Минимальный срок: 6 месяцев' });
max(path.loanTerm, 360, { message: 'Максимальный срок: 360 месяцев (30 лет)' });
// ==========================================
// Цель кредита
// ==========================================
required(path.loanPurpose, { message: 'Цель кредита обязательна' });
minLength(path.loanPurpose, 10, { message: 'Пожалуйста, укажите не менее 10 символов' });
maxLength(path.loanPurpose, 500, { message: 'Максимум 500 символов' });
// Условная валидация будет добавлена дальше...
};
Условная валидация: Поля ипотеки
Добавьте валидацию для полей, связанных с ипотекой, используя applyWhen:
src/schemas/validators/loan-info.ts
export const loanValidation: ValidationSchemaFn<CreditApplicationForm> = (path) => {
// ... предыдущая валидация ...
// ==========================================
// Условно: Поля ипотеки
// ==========================================
applyWhen(
path.loanType,
(loanType) => loanType === 'mortgage',
(p) => {
// Стоимость имущества - обязательно для ипотеки
required(p.propertyValue, { message: 'Стоимость имущества обязательна для ипотеки' });
min(p.propertyValue, 1000000, { message: 'Минимальная стоимость имущества: 1 000 000' });
max(p.propertyValue, 500000000, { message: 'Максимальная стоимость имущества: 500 000 000' });
// Первоначальный платёж - обязателен для ипотеки
required(p.initialPayment, { message: 'Первоначальный платёж обязателен для ипотеки' });
min(p.initialPayment, 100000, { message: 'Минимальный первоначальный платёж: 100 000' });
}
);
};
Условная валидация: Поля автокредита
Добавьте валидацию для полей, связанных с автокредитом:
src/schemas/validators/loan-info.ts
export const loanValidation: ValidationSchemaFn<CreditApplicationForm> = (path) => {
// ... предыдущая валидация ...
// ==========================================
// Условно: Поля автокредита
// ==========================================
const currentYear = new Date().getFullYear();
applyWhen(
path.loanType,
(loanType) => loanType === 'car',
(p) => {
// Марка автомобиля
required(p.carBrand, { message: 'Марка автомобиля обязательна' });
// Модель автомобиля
required(p.carModel, { message: 'Модель автомобиля обязательна' });
// Год выпуска
required(p.carYear, { message: 'Год выпуска обязателен' });
min(p.carYear, 2000, { message: 'Автомобиль должен быть 2000 года или новее' });
max(p.carYear, currentYear + 1, { message: `Максимальный год: ${currentYear + 1}` });
// Цена автомобиля
required(p.carPrice, { message: 'Цена автомобиля обязательна' });
min(p.carPrice, 100000, { message: 'Минимальная цена автомобиля: 100 000' });
max(p.carPrice, 20000000, { message: 'Максимальная цена автомобиля: 20 000 000' });
}
);
};
Полный код
Вот полный валидатор для Шага 1:
src/schemas/validators/loan-info.ts
import { required, min, max, minLength, maxLength, applyWhen } from '@reformer/core/validators';
import type { ValidationSchemaFn, FieldPath } from '@reformer/core';
import type { CreditApplicationForm } from '@/types';
/**
* Валидация для Шага 1: Информация о кредите
*
* Валидирует:
* - Обязательные поля (loanType, loanAmount, loanTerm, loanPurpose)
* - Числовые диапазоны (сумма, срок)
* - Условные поля ипотеки (propertyValue, initialPayment)
* - Условные поля автокредита (carBrand, carModel, carYear, carPrice)
*/
export const loanValidation: ValidationSchemaFn<CreditApplicationForm> = (
path: FieldPath<CreditApplicationForm>
) => {
// ==========================================
// Тип кредита
// ==========================================
required(path.loanType, { message: 'Пожалуйста, выберите тип кредита' });
// ==========================================
// Сумма кредита
// ==========================================
required(path.loanAmount, { message: 'Сумма кредита обязательна' });
min(path.loanAmount, 50000, { message: 'Минимальная сумма: 50 000' });
max(path.loanAmount, 10000000, { message: 'Максимальная сумма: 10 000 000' });
// ==========================================
// Срок кредита
// ==========================================
required(path.loanTerm, { message: 'Срок кредита обязателен' });
min(path.loanTerm, 6, { message: 'Минимальный срок: 6 месяцев' });
max(path.loanTerm, 360, { message: 'Максимальный срок: 360 месяцев (30 лет)' });
// ==========================================
// Цель кредита
// ==========================================
required(path.loanPurpose, { message: 'Цель кредита обязательна' });
minLength(path.loanPurpose, 10, { message: 'Пожалуйста, укажите не менее 10 символов' });
maxLength(path.loanPurpose, 500, { message: 'Максимум 500 символов' });
// ==========================================
// Условно: Поля ипотеки
// ==========================================
applyWhen(
path.loanType,
(loanType) => loanType === 'mortgage',
(p) => {
required(p.propertyValue, { message: 'Стоимость имущества обязательна для ипотеки' });
min(p.propertyValue, 1000000, { message: 'Минимальная стоимость имущества: 1 000 000' });
max(p.propertyValue, 500000000, { message: 'Максимальная стоимость имущества: 500 000 000' });
required(p.initialPayment, { message: 'Первоначальный платёж обязателен для ипотеки' });
min(p.initialPayment, 100000, { message: 'Минимальный первоначальный платёж: 100 000' });
}
);
// ==========================================
// Условно: Поля автокредита
// ==========================================
const currentYear = new Date().getFullYear();
applyWhen(
path.loanType,
(loanType) => loanType === 'car',
(p) => {
required(p.carBrand, { message: 'Марка автомобиля обязательна' });
required(p.carModel, { message: 'Модель автомобиля обязательна' });
required(p.carYear, { message: 'Год выпуска обязателен' });
min(p.carYear, 2000, { message: 'Автомобиль должен быть 2000 года или новее' });
max(p.carYear, currentYear + 1, { message: `Максимальный год: ${currentYear + 1}` });
required(p.carPrice, { message: 'Цена автомобиля обязательна' });
min(p.carPrice, 100000, { message: 'Минимальная цена автомобиля: 100 000' });
max(p.carPrice, 20000000, { message: 'Максимальная цена автомобиля: 20 000 000' });
}
);
};
Как это работает
Валидаторы required
required(path.loanAmount, { message: 'Сумма кредита обязательна' });
- Срабатывает когда поле пусто, null или undefined
- Показывает сообщение об ошибке сразу когда пользователь оставляет поле пустым
Валидаторы диапазонов
min(path.loanAmount, 50000, { message: 'Минимальная сумма: 50 000' });
max(path.loanAmount, 10000000, { message: 'Максимальная сумма: 10 000 000' });
min: Срабатывает когда значение < минимумаmax: Срабатывает когда значение > максимума- Работает с числами и числовыми строками
Условная валидация с applyWhen
applyWhen(
path.loanType, // Поле для отслеживания (зависимость)
(loanType) => loanType === 'mortgage', // Функция условия
(p) => {
// Валидация, которая применяется только когда условие истинно
required(p.propertyValue, { message: 'Стоимость имущества обязательна' });
min(p.propertyValue, 1000000, { message: 'Минимальная стоимость: 1 000 000' });
}
);
- Первый аргумент: Поле для отслеживания (зависимость)
- Второй аргумент: Функция условия
- Третий аргумент: Функция с валидацией, которая применяется условно
- Все валидаторы внутри колбэка применяются только когда условие возвращает
true
Интеграция с Behaviors
Помните из раздела Behaviors, у нас есть:
// Behavior скрывает поля ипотеки когда не нужны
enableWhen(path.propertyValue, path.loanType, (type) => type === 'mortgage');
// Валидация применяется только когда условие истинно
applyWhen(
path.loanType,
(type) => type === 'mortgage',
(p) => {
required(p.propertyValue, { message: 'Стоимость имущества обязательна' });
}
);
Когда loanType не 'mortgage':
- Behavior скрывает поле → Пользователь его не видит
- Валидация пропускает поле → Ошибки не показываются
Идеальная синхронизация!
Тестирование валидации
Протестируйте эти сценарии:
Обязательные поля
- Попытка отправить без выбора типа кредита → Ошибка показана
- Попытка отправить без суммы кредита → Ошибка показана
- Попытка отправить без срока → Ошибка показана
- Попытка отправить без цели → Ошибка показана
Числовые диапазоны
- Введите сумму кредита < 50 000 → Ошибка показана
- Введите сумму кредита > 10 000 000 → Ошибка показана
- Введите срок < 6 → Ошибка показана
- Введите срок > 360 → Ошибка показана
Длина строки
- Введите цель с < 10 символами → Ошибка показана
- Введите цель с > 500 символами → Ошибка показана
Условно: Ипотека
- Выберите тип кредита = 'mortgage' → propertyValue и initialPayment становятся обязательны
- Оставьте propertyValue пусто → Ошибка показана
- Введите propertyValue < 1 000 000 → Ошибка показана
- Оставьте initialPayment пусто → Ошибка показана
Условно: Автокредит
- Выберите тип кредита = 'car' → Поля автомобиля становятся обязательны
- Оставьте carBrand пусто → Ошибка показана
- Оставьте carModel пусто → Ошибка показана
- Введите carYear < 2000 → Ошибка показана
- Введите carPrice < 100 000 → Ошибка показана
Переключение типов кредита
- Заполните поля ипотеки → Переключитесь на 'car' → Ошибки ипотеки исчезают
- Заполните поля автокредита → Переключитесь на 'mortgage' → Ошибки автокредита исчезают
Ключевые выводы
- Декларативные правила - Ясные, лаконичные определения валидации
- Условная валидация - Используйте
applyWhenдля группировки условных правил - Работает с Behaviors - Скрытые поля не валидируются
- Типобезопасно - Полная поддержка TypeScript для путей полей
- Локализировано - Вся валидация Шага 1 в одном файле
Что дальше?
В следующем разделе мы добавим валидацию для Шага 2: Личная информация, включая:
- Валидацию имён с паттернами кириллицы
- Валидацию даты рождения с расчётом возраста
- Валидацию формата паспорта
- Валидацию паттерна ИНН и СНИЛС
- Пользовательские валидаторы для сложных правил
Паттерны валидации, которые мы выучили здесь, будут применены во всей форме!