Обработка ошибок валидации

При работе с Choices.js валидация данных обычно выходит за пределы стандартной HTML-валидации, поскольку библиотека управляет состоянием выбора самостоятельно. Ошибки формируются не только на уровне DOM, но и на уровне внутреннего состояния компонента, событий и внешних правил бизнес-логики.

Выделяются несколько категорий ошибок:

  • Синтаксические ошибки ввода — некорректный формат строки поиска или значения при пользовательском вводе.
  • Логические ошибки выбора — нарушение правил допустимых комбинаций значений.
  • Ограничения коллекции — превышение maxItemCount, попытка добавить дубликаты, нарушение уникальности.
  • Серверные ошибки — отказ API при асинхронной загрузке или проверке.
  • Ошибки синхронизации состояния — расхождение между внешним состоянием приложения и внутренним состоянием Choices.js.

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


Механизм отслеживания ошибок через события

Choices.js предоставляет событийную модель, которая является основой для построения системы валидации.

Ключевые события:

  • addItem
  • removeItem
  • highlightItem
  • search
  • choice
  • error

На практике основную роль в обработке ошибок играют события addItem и пользовательские проверки, выполняемые до или после изменения состояния.

Пример логики перехвата ошибок при добавлении:

const instance = new Choices('#select');

instance.passedElement.element.addEventListener('addItem', (event) => {
  const value = event.detail.value;

  if (value.length < 3) {
    instance.removeActiveItemsByValue(value);
    console.warn('Ошибка валидации: значение слишком короткое');
  }
});

Такой подход позволяет реализовать синхронную проверку и немедленное отклонение некорректного состояния.


Предвалидация перед добавлением элементов

Одним из наиболее стабильных подходов является перехват данных до их попадания в внутренний state библиотеки.

Основные стратегии:

  • фильтрация входного значения в обработчике search
  • проверка в момент addItem
  • использование внешней функции-валидатора
function validateValue(value) {
  return /^[a-zA-Z0-9_-]+$/.test(value);
}

const instance = new Choices('#select', {
  addItemFilter: (value) => validateValue(value)
});

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


Обработка дубликатов и ограничений уникальности

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

Типичный алгоритм:

instance.passedElement.element.addEventListener('addItem', (event) => {
  const value = event.detail.value;

  const items = instance.getValue(true);

  const isDuplicate = items.includes(value);

  if (isDuplicate) {
    instance.removeActiveItemsByValue(value);
  }
});

Расширенная версия включает:

  • проверку case-insensitive
  • нормализацию Unicode
  • сравнение по ID, а не по label

Обработка ошибок максимального количества элементов

Ограничение maxItemCount часто становится источником ошибок пользовательского интерфейса. Библиотека предотвращает добавление лишних элементов, но не формирует пользовательские сообщения.

const instance = new Choices('#select', {
  maxItemCount: 3
});

instance.passedElement.element.addEventListener('addItem', (event) => {
  const items = instance.getValue(true);

  if (items.length > 3) {
    instance.removeActiveItemsByValue(event.detail.value);
    showError('Превышено максимальное количество элементов');
  }
});

В крупных приложениях ошибка связывается с UI-слоем, а не с самим компонентом.


Асинхронная валидация и серверные ошибки

В сценариях, где данные проверяются через API, обработка ошибок требует асинхронного контроля.

Типичный паттерн:

instance.passedElement.element.addEventListener('addItem', async (event) => {
  const value = event.detail.value;

  try {
    const response = await fetch('/validate', {
      method: 'POST',
      body: JSON.stringify({ value })
    });

    const result = await response.json();

    if (!result.valid) {
      instance.removeActiveItemsByValue(value);
      displayServerError(result.message);
    }
  } catch (e) {
    instance.removeActiveItemsByValue(value);
    displayServerError('Ошибка соединения с сервером');
  }
});

В этом случае важным становится контроль состояния гонок (race conditions), особенно при быстром вводе пользователя.


Синхронизация состояния и внешняя модель данных

Choices.js не является источником истины в архитектуре приложения. Ошибки часто возникают при рассинхронизации внешнего состояния и внутреннего списка выбранных значений.

Подходы к решению:

  • хранение источника данных вне библиотеки
  • реактивное обновление через события
  • принудительная синхронизация через setChoiceByValue
function syncState(values) {
  instance.clearStore();
  instance.setChoiceByValue(values);
}

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


Пользовательские сообщения об ошибках

Библиотека не предоставляет встроенной системы сообщений об ошибках, поэтому слой отображения строится отдельно.

Основные стратегии:

  • DOM-элементы под контролом формы
  • интеграция с UI-фреймворком
  • централизованный error store

Пример:

function showError(message) {
  const el = document.querySelector('.error-box');
  el.textContent = message;
  el.classList.add('visible');
}

Сообщения должны быть привязаны к конкретному состоянию выбора, а не к абстрактному событию.


Контроль ошибок поиска и фильтрации

Ошибки могут возникать на этапе поиска, когда пользователь вводит данные, а Choices.js фильтрует список.

instance.passedElement.element.addEventListener('search', (event) => {
  const query = event.detail.value;

  if (query.length > 50) {
    console.warn('Слишком длинный запрос поиска');
  }
});

В сложных системах фильтрация переносится на сервер, а локальный поиск отключается через shouldSort и кастомные адаптеры.


Защита от неконсистентных состояний

Основные причины неконсистентности:

  • параллельные async операции
  • повторная инициализация экземпляра
  • прямое изменение DOM вне API

Решение:

  • уничтожение старого экземпляра через destroy
  • строгий контроль единственного источника состояния
  • блокировка UI во время обновлений
instance.destroy();
instance = new Choices('#select');

Логирование и диагностика ошибок

При промышленной эксплуатации важно фиксировать все сбои:

function logError(context, error) {
  console.error('[Choices validation error]', context, error);
}

Рекомендуется логировать:

  • значение, вызвавшее ошибку
  • тип события
  • текущее состояние списка
  • ответ сервера при асинхронной проверке

Интеграция с внешними валидаторами

Choices.js часто используется совместно с формами, где уже существует слой валидации (например, Yup, Joi или кастомные схемы).

Интеграция выполняется через адаптер:

function externalValidator(value) {
  return schema.validate(value);
}

instance.passedElement.element.addEventListener('addItem', (event) => {
  const result = externalValidator(event.detail.value);

  if (!result.valid) {
    instance.removeActiveItemsByValue(event.detail.value);
  }
});

Обработка каскадных ошибок в сложных формах

В формах с несколькими зависимыми селектами ошибка одного поля может влиять на другие.

Стратегии:

  • централизованный state manager
  • подписка на изменения всех экземпляров Choices.js
  • пересчёт валидности всей формы при каждом изменении
function validateForm() {
  const values = instance.getValue(true);

  if (values.includes('invalid')) {
    setFormInvalid();
  }
}

Устойчивость к ошибкам пользовательского ввода

Особое внимание уделяется защите от:

  • вставки большого объёма данных
  • быстрых последовательных событий
  • некорректных Unicode-символов

Решение включает:

  • debounce на search
  • throttling событий
  • нормализацию входных строк
function normalize(value) {
  return value.trim().toLowerCase();
}

Архитектурная модель обработки ошибок

В зрелых системах с Choices.js выделяется отдельный слой:

  • UI layer (отображение ошибок)
  • validation layer (правила)
  • adapter layer (интеграция с Choices.js)
  • data layer (источник истины)

Такая структура позволяет изолировать библиотеку от бизнес-логики и минимизировать побочные эффекты при изменении состояния.