Шаг 3: Валидация контактной информации
Валидация email, телефона и полей адреса с форматными валидаторами и условными правилами.
Что мы валидируем
Шаг 3 содержит контактные и адресные поля:
| Поле | Правила валидации |
|---|---|
phoneMain | Обязательно, формат телефона |
phoneAdditional | Опционально, формат телефона |
email | Обязательно, формат email |
emailAdditional | Опционально, формат email |
registrationAddress.city | Обязательно |
registrationAddress.street | Обязательно |
registrationAddress.house | Обязательно |
registrationAddress.postalCode | Опционально, 6 цифр |
residenceAddress.* | Обязательно когда sameAsRegistration false |
Создание файла валидатора
Создайте файл валидатора для Шага 3:
touch src/schemas/validators/contact-info.ts
Реализация
Валидация телефона и email
Начните с валидации формата телефона и email:
import { required, email, phone, pattern, applyWhen } from '@reformer/core/validators';
import type { ValidationSchemaFn, FieldPath } from '@reformer/core';
import type { CreditApplicationForm } from '@/types';
/**
* Валидация для Шага 3: Контактная информация
*
* Валидирует:
* - Номера телефонов (главный и дополнительный)
* - Адреса email (главный и дополнительный)
* - Адрес регистрации (всегда обязателен)
* - Адрес проживания (условно обязателен)
*/
export const contactValidation: ValidationSchemaFn<CreditApplicationForm> = (
path: FieldPath<CreditApplicationForm>
) => {
// ==========================================
// Номера телефонов
// ==========================================
// Главный телефон (обязателен)
required(path.phoneMain, { message: 'Главный номер телефона обязателен' });
phone(path.phoneMain, { message: 'Неверный формат телефона' });
// Дополнительный телефон (опционален, но должен быть валидным если указан)
phone(path.phoneAdditional, { message: 'Неверный формат телефона' });
// ==========================================
// Адреса email
// ==========================================
// Главный email (обязателен)
required(path.email, { message: 'Email обязателен' });
email(path.email, { message: 'Неверный формат email' });
// Дополнительный email (опционален, но должен быть валидным если указан)
email(path.emailAdditional, { message: 'Неверный формат email' });
};
Форматные валидаторы как email() и phone() автоматически пропускают пустые значения. Поэтому мы используем required() отдельно для обязательных полей.
Валидация адреса регистрации
Добавьте валидацию для адреса регистрации (всегда обязателен):
export const contactValidation: ValidationSchemaFn<CreditApplicationForm> = (path) => {
// ... предыдущая валидация ...
// ==========================================
// Адрес регистрации (Всегда обязателен)
// ==========================================
required(path.registrationAddress.city, { message: 'Город обязателен' });
required(path.registrationAddress.street, { message: 'Улица обязательна' });
required(path.registrationAddress.house, { message: 'Номер дома обязателен' });
// Квартира опциональна, валидация не требуется
// Почтовый код (опционален, но должен быть 6 цифр если указан)
pattern(path.registrationAddress.postalCode, /^\d{6}$/, {
message: 'Почтовый код должен быть 6 цифр',
});
};
Условная валидация адреса проживания
Добавьте условную валидацию для адреса проживания (требуется только когда отличается от регистрации):
export const contactValidation: ValidationSchemaFn<CreditApplicationForm> = (path) => {
// ... предыдущая валидация ...
// ==========================================
// Адрес проживания (Условно обязателен)
// ==========================================
applyWhen(
path.sameAsRegistration,
(same) => !same,
(p) => {
required(p.residenceAddress.city, { message: 'Город обязателен' });
required(p.residenceAddress.street, { message: 'Улица обязательна' });
required(p.residenceAddress.house, { message: 'Номер дома обязателен' });
}
);
// Квартира опциональна
// Почтовый код (опционален, но должен быть 6 цифр если указан)
pattern(path.residenceAddress.postalCode, /^\d{6}$/, {
message: 'Почтовый код должен быть 6 цифр',
});
};
Полный код
Вот полный валидатор для Шага 3:
import { required, email, phone, pattern, applyWhen } from '@reformer/core/validators';
import type { ValidationSchemaFn, FieldPath } from '@reformer/core';
import type { CreditApplicationForm } from '@/types';
/**
* Валидация для Шага 3: Контактная информация
*
* Валидирует:
* - Номера телефонов (главный и дополнительный)
* - Адреса email (главный и дополнительный)
* - Адрес регистрации (всегда обязателен)
* - Адрес проживания (условно обязателен)
*/
export const contactValidation: ValidationSchemaFn<CreditApplicationForm> = (
path: FieldPath<CreditApplicationForm>
) => {
// ==========================================
// Номера телефонов
// ==========================================
required(path.phoneMain, { message: 'Главный номер телефона обязателен' });
phone(path.phoneMain, { message: 'Неверный формат телефона' });
phone(path.phoneAdditional, { message: 'Неверный формат телефона' });
// ==========================================
// Адреса email
// ==========================================
required(path.email, { message: 'Email обязателен' });
email(path.email, { message: 'Неверный формат email' });
email(path.emailAdditional, { message: 'Неверный формат email' });
// ==========================================
// Адрес регистрации (Всегда обязателен)
// ==========================================
required(path.registrationAddress.city, { message: 'Город обязателен' });
required(path.registrationAddress.street, { message: 'Улица обязательна' });
required(path.registrationAddress.house, { message: 'Номер дома обязателен' });
pattern(path.registrationAddress.postalCode, /^\d{6}$/, {
message: 'Почтовый код должен быть 6 цифр',
});
// ==========================================
// Адрес проживания (Условно обязателен)
// ==========================================
requiredWhen(path.residenceAddress.city, path.sameAsRegistration, (same) => !same, {
message: 'Город обязателен',
});
requiredWhen(path.residenceAddress.street, path.sameAsRegistration, (same) => !same, {
message: 'Улица обязательна',
});
requiredWhen(path.residenceAddress.house, path.sameAsRegistration, (same) => !same, {
message: 'Номер дома обязателен',
});
pattern(path.residenceAddress.postalCode, /^\d{6}$/, {
message: 'Почтовый код должен быть 6 цифр',
});
};
Как это работает
Валидатор email
email(path.email, { message: 'Неверный формат email' });
- Встроенная валидация формата email
- Проверяет базовую структуру email:
user@domain.com - Пропускает пустые значения (не срабатывает на пустых полях)
- Используйте с
required()для обязательных полей
Валидатор телефона
phone(path.phoneMain, { message: 'Неверный формат телефона' });
- Встроенная валидация формата телефона
- Поддерживает различные форматы:
+7 (999) 123-45-67+79991234567899912345679991234567
- Пропускает пустые значения
- Используйте с
required()для обязательных полей
Условно обязательно
applyWhen(
path.sameAsRegistration, // ← Отслеживайте это поле
(same) => !same, // ← Условие: применяется когда false
(p) => {
// ← Применяйте валидацию когда условие true
required(p.residenceAddress.city, { message: 'Город обязателен' });
required(p.residenceAddress.street, { message: 'Улица обязательна' });
required(p.residenceAddress.house, { message: 'Номер дома обязателен' });
}
);
Как это работает:
- Отслеживает поле
sameAsRegistration - Когда
sameAsRegistrationизменяется, переоценивает условие - Если условие возвращает
true, применяет валидацию внутри блока - Если условие возвращает
false, валидация не применяется
Интеграция с Behaviors
Помните из раздела Behaviors:
// Behavior: Скрыть адрес проживания когда совпадает с регистрацией
disableWhen(path.residenceAddress, path.sameAsRegistration, (same) => same === true);
// Behavior: Отключить адрес проживания когда совпадает с регистрацией
disableWhen(path.residenceAddress, path.sameAsRegistration, (same) => same === true);
// Валидация: Требовать адрес проживания когда отличается
applyWhen(
path.sameAsRegistration,
(same) => !same,
(p) => {
required(p.residenceAddress.city, { message: 'Город обязателен' });
}
);
Идеальная синхронизация:
- Когда
sameAsRegistration = true→ Поле скрыто и не требуется - Когда
sameAsRegistration = false→ Поле видимо и требуется
Тестирование валидации
Протестируйте эти сценарии:
Валидация телефона
- Оставьте главный телефон пусто → Ошибка показана
- Введите неверный формат главного телефона → Ошибка показана
- Введите валидный главный телефон → Ошибки нет
- Введите неверный дополнительный телефон → Ошибка показана (хотя опционален)
- Оставьте дополнительный телефон пусто → Ошибки нет
Валидация email
- Оставьте главный email пусто → Ошибка показана
- Введите неверный формат email (без @) → Ошибка показана
- Введите неверный формат email (без домена) → Ошибка показана
- Введите валидный email → Ошибки нет
- Введите неверный дополнительный email → Ошибка показана (хотя опционален)
- Оставьте дополнительный email пусто → Ошибки нет
Адрес регистрации
- Оставьте город пусто → Ошибка показана
- Оставьте улицу пусто → Ошибка показана
- Оставьте дом пусто → Ошибка показана
- Оставьте квартиру пусто → Ошибки нет (опционально)
- Введите неверный почтовый код (5 цифр) → Ошибка показана
- Введите неверный почтовый код (буквы) → Ошибка показана
- Введите валидный почтовый код (6 цифр) → Ошибки нет
- Оставьте почтовый код пусто → Ошибки нет (опционально)
Адрес проживания
- Отметьте "совпадает с регистрацией" → Поля проживания не требуются
- Отмените "совпадает с регистрацией" → Поля проживания становятся требуемыми
- Оставьте город проживания пусто (когда отличается) → Ошибка показана
- Оставьте улицу проживания пусто (когда отличается) → Ошибка показана
- Оставьте дом проживания пусто (когда отличается) → Ошибка показана
Поддерживаемые форматы телефонов
Валидатор phone() принимает различные форматы:
// Все валидные форматы:
'+7 (999) 123-45-67';
'+79991234567';
'89991234567';
'9991234567';
'+1 (234) 567-8901'; // Международный
Поддерживаемые форматы email
Валидатор email() следует стандартному формату email:
// Валидные emails:
'user@example.com';
'user.name@example.com';
'user+tag@example.co.uk';
'user_name123@sub.example.org';
// Невалидные emails:
'user@'; // Нет домена
'@example.com'; // Нет пользователя
'user @example.com'; // Пробел в имени пользователя
'user@example'; // Нет TLD
Ключевые выводы
- Форматные валидаторы - Используйте встроенные
email()иphone()для форматов - Отдельно требуемое - Форматные валидаторы пропускают пустые значения
- Условно требуемое - Используйте
requiredWhen()для динамических требований - Работает с Behaviors - Скрытые/отключённые поля пропускают валидацию
- Опциональная валидация - Можно валидировать формат даже когда не требуется
Распространённые паттерны
Требуемый email
required(path.email, { message: 'Email обязателен' });
email(path.email, { message: 'Неверный формат email' });
Опциональный email (валидирует формат если указан)
email(path.emailAdditional, { message: 'Неверный формат email' });
// Нет required() - поле опционально
Условно требуемые поля с applyWhen
applyWhen(
path.condition,
(value) => value === true,
(p) => {
required(p.field, { message: 'Поле обязательно' });
}
);
Русский почтовый код
pattern(path.postalCode, /^\d{6}$/, {
message: 'Почтовый код должен быть 6 цифр',
});
Что дальше?
В следующем разделе мы добавим валидацию для Шага 4: Занятость, включая:
- Требуемый статус занятости
- Условную валидацию для работающих vs самозанятых
- Валидацию дохода с минимальными пороговыми значениями
- Валидацию стажа работы
- Валидацию полей, специфических для бизнеса
Мы продолжим использовать паттерны условной валидации, которые выучили здесь!