В библиотеке Pikaday механизм выбора даты изначально строится вокруг
ограничений диапазона и функции отключения отдельных дней. Однако в
реальных приложениях этого недостаточно: бизнес-правила часто требуют
сложной логики проверки, выходящей за рамки minDate и
maxDate. Кастомные валидаторы формируют дополнительный
слой, который превращает календарь в управляемую систему правил.
Ключевая идея заключается в разделении ответственности: календарь отвечает за выбор даты, а валидаторы — за допустимость выбранного значения в конкретном контексте доменной логики.
В Pikaday существует несколько встроенных механизмов, которые выступают основой для построения кастомной валидации:
minDate и maxDate — ограничение
диапазонаdisableDayFn — функция отключения конкретных датonSelect — обработчик выбора значенияparse и toString — контроль преобразования
данныхЭти инструменты не являются полноценной системой валидации, но позволяют внедрить первичный фильтр значений до передачи данных в форму.
Кастомный валидатор обычно реализуется как функция, принимающая дату и контекст, и возвращающая булево значение или объект результата проверки.
Базовый шаблон:
function validator(date, context) {
return true; // допустимо
}
Расширенный вариант с диагностикой:
function validator(date, context) {
if (date.getDay() === 0) {
return { valid: false, reason: 'sunday_not_allowed' };
}
return { valid: true };
}
Контекст позволяет учитывать дополнительные параметры: текущий пользовательский ввод, состояние формы, внешние ограничения.
Наиболее прямой способ подключения логики в Pikaday — использование
disableDayFn. Эта функция вызывается для каждой
отображаемой даты и позволяет блокировать её на уровне интерфейса.
const picker = new Pikaday({
field: document.querySelector('#date'),
disableDayFn: function(date) {
return isInvalidByBusinessRules(date);
}
});
В этой модели валидатор становится синхронным предикатом, а календарь автоматически скрывает недопустимые дни.
При усложнении логики возникает необходимость объединять несколько правил. Комбинация валидаторов строится через последовательное применение функций.
function composeValidators(validators) {
return function(date, context) {
for (let i = 0; i < validators.length; i++) {
const result = validators[i](date, context);
if (result !== true && result.valid === false) {
return result;
}
}
return { valid: true };
};
}
Такой подход позволяет разделять ответственность между независимыми модулями:
В сложных интерфейсах допустимость даты зависит от состояния формы. Например, дата окончания не может быть раньше даты начала. В этом случае валидатор получает доступ к внешнему состоянию.
function createRangeValidator(getStartDate) {
return function(date) {
const start = getStartDate();
if (start && date < start) {
return { valid: false, reason: 'end_before_start' };
}
return { valid: true };
};
}
В Pikaday подобная логика обычно синхронизируется через
onSelect, обновляющий состояние формы и
переинициализирующий проверку.
Ключевой архитектурный принцип заключается в том, что визуальная блокировка дат и логическая проверка не должны быть идентичны.
UI-валидация через disableDayFn отвечает за
предотвращение неверного выбора.
Бизнес-валидация после onSelect отвечает за
подтверждение корректности данных.
picker.on('select', function(date) {
const result = validate(date);
if (!result.valid) {
showError(result.reason);
}
});
Такой подход предотвращает ситуацию, когда интерфейс скрывает часть ошибок, которые всё равно должны быть обработаны системой.
Кастомные валидаторы часто требуют нормализации даты перед проверкой.
В Pikaday входные значения могут приходить как строки или объекты
Date, в зависимости от конфигурации parse.
Нормализация:
function normalize(date) {
return (date instanceof Date) ? date : new Date(date);
}
Это снижает риск неконсистентного поведения валидаторов.
При усложнении логики проверки формируется многоуровневая система:
Каждый уровень может быть представлен отдельным валидатором, что упрощает тестирование и сопровождение.
Структурированная система валидаторов должна возвращать не только факт ошибки, но и её причину. Это позволяет строить интерфейсы с объяснением поведения.
return {
valid: false,
reason: 'holiday_blocked',
meta: {
date
}
};
Такая модель полезна для интеграции с формами, где требуется отображение конкретного сообщения.
В Pikaday нет встроенного реактивного слоя, поэтому изменения валидаторов часто требуют пересоздания экземпляра календаря или ручного обновления состояния.
Это приводит к архитектурному решению: валидаторы должны быть детерминированными и не хранить внутреннего состояния, чтобы их можно было безопасно пересчитывать при каждом изменении внешних параметров.
Функция disableDayFn вызывается для большого количества
дат при рендеринге месяца, поэтому валидаторы должны быть
оптимизированы:
const holidaysSet = new Set(holidays.map(h => h.toISOString()));
function isHoliday(date) {
return holidaysSet.has(date.toISOString());
}
Такой подход уменьшает стоимость повторных вычислений при отрисовке календаря.
Наиболее устойчивый подход — построение слоя валидаторов как независимого модуля:
В связке с Pikaday это позволяет превратить базовый календарь в гибкую систему правил, адаптированную под сложные формы и бизнес-процессы.