Кастомные валидаторы

В библиотеке 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 };
}

Контекст позволяет учитывать дополнительные параметры: текущий пользовательский ввод, состояние формы, внешние ограничения.

Интеграция с disableDayFn

Наиболее прямой способ подключения логики в 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-валидации и бизнес-логики

Ключевой архитектурный принцип заключается в том, что визуальная блокировка дат и логическая проверка не должны быть идентичны.

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);
}

Это снижает риск неконсистентного поведения валидаторов.

Сценарии многоуровневых правил

При усложнении логики проверки формируется многоуровневая система:

  1. Проверка диапазона дат
  2. Проверка календарных исключений
  3. Проверка бизнес-ограничений
  4. Проверка зависимостей между полями
  5. Финальная агрегация результата

Каждый уровень может быть представлен отдельным валидатором, что упрощает тестирование и сопровождение.

Ошибки и причины отказа

Структурированная система валидаторов должна возвращать не только факт ошибки, но и её причину. Это позволяет строить интерфейсы с объяснением поведения.

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 это позволяет превратить базовый календарь в гибкую систему правил, адаптированную под сложные формы и бизнес-процессы.