Обработка некорректных данных

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

Slim Select работает с массивом объектов вида:

[
  { text: 'Option 1', value: '1' },
  { text: 'Option 2', value: '2' }
]

Критичными являются два поля:

  • text — отображаемая строка
  • value — уникальный идентификатор

Нарушение этой структуры создаёт каскадные проблемы, особенно при динамической загрузке или преобразовании данных из API.

Типовые нарушения:

  • отсутствует value
  • отсутствует text
  • value не уникален
  • типы данных не строковые (числа, объекты без приведения)
  • вложенные или лишние поля ломают сериализацию
  • null или undefined в массиве

Фильтрация некорректных элементов

Первый уровень защиты — предварительная очистка массива до инициализации компонента.

function sanitizeOptions(options) {
  if (!Array.isArray(options)) return [];

  return options
    .filter(item => item && typeof item === 'object')
    .filter(item => item.text != null && item.value != null)
    .map(item => ({
      text: String(item.text).trim(),
      value: String(item.value)
    }));
}

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

Обработка null, undefined и пустых значений

Особую опасность представляют «тихие» значения:

  • undefined не вызывает ошибок напрямую, но ломает рендер
  • null может попадать в DOM как строка
  • пустые строки создают визуально неразличимые элементы

Расширенная нормализация:

function normalizeValue(value) {
  if (value === null || value === undefined) return '';
  return String(value);
}

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

Защита от дубликатов значений

Slim Select не гарантирует уникальность value. При наличии дублей:

  • выбирается первая совпавшая опция
  • последующие становятся недоступными для корректного связывания состояния
  • set-операции могут вести себя непредсказуемо

Удаление дубликатов:

function uniqueOptions(options) {
  const seen = new Set();

  return options.filter(item => {
    if (seen.has(item.value)) return false;
    seen.add(item.value);
    return true;
  });
}

В сложных интерфейсах важно учитывать, что дубликаты часто появляются не в исходных данных, а после объединения нескольких источников.

Очистка вложенных и лишних полей

API часто возвращают расширенные объекты:

{
  id: 1,
  name: 'Option',
  meta: { ... },
  permissions: [...]
}

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

Корректное преобразование:

function mapToSlimSelect(data) {
  return data.map(item => ({
    text: item.name ?? '',
    value: item.id ?? ''
  }));
}

Важно избегать передачи meta-данных внутрь Slim Select — они не используются и увеличивают риск побочных эффектов при сериализации.

Работа с некорректными асинхронными данными

При загрузке данных через API основная проблема — гонки запросов и частичные ответы.

Сценарии:

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

Защита через проверку актуальности:

let requestId = 0;

async function loadOptions(url) {
  const currentRequest = ++requestId;

  const response = await fetch(url);
  const data = await response.json();

  if (currentRequest !== requestId) return [];

  return sanitizeOptions(data);
}

Такой подход предотвращает перезапись состояния устаревшими данными.

Обработка пустых массивов

Пустой массив не является ошибкой, но может ломать UX при отсутствии fallback-состояния.

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

const options = sanitizeOptions(data);

if (options.length === 0) {
  slimSelect.setData([{ text: 'Нет данных', value: '', disabled: true }]);
} else {
  slimSelect.setData(options);
}

Ключевой момент — явное добавление disabled-элемента вместо пустого состояния.

Защита от HTML-инъекций в текстовых полях

Slim Select рендерит text в DOM, и некорректные данные могут привести к вставке HTML.

Проблемные случаи:

  • <script> в text
  • HTML-теги в названиях
  • неожиданные сущности

Базовая защита:

function escapeHtml(str) {
  return String(str)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

Применение:

function safeOptions(data) {
  return data.map(item => ({
    text: escapeHtml(item.text),
    value: String(item.value)
  }));
}

Контроль типов при динамическом обновлении данных

При использовании setData важно учитывать, что Slim Select не валидирует вход повторно. Это означает, что некорректные данные могут быть «закреплены» внутри состояния.

Рекомендуемый подход:

function updateSelect(slimSelect, data) {
  const cleaned = uniqueOptions(
    sanitizeOptions(data)
  );

  slimSelect.setData(cleaned);
}

Обработка частично повреждённых структур

Реальные API часто возвращают смешанные массивы:

[
  { text: 'A', value: '1' },
  null,
  { value: '2' },
  { text: 'C' },
  'invalid'
]

Жёсткая фильтрация обязательна:

function strictSanitize(options) {
  return (options || [])
    .filter(item => item && typeof item === 'object')
    .filter(item => typeof item.text === 'string' || typeof item.text === 'number')
    .filter(item => typeof item.value === 'string' || typeof item.value === 'number')
    .map(item => ({
      text: String(item.text),
      value: String(item.value)
    }));
}

Стабилизация состояния выбранного значения

При некорректных данных часто возникает проблема: выбранное значение не совпадает ни с одной опцией после обновления массива.

Решение — проверка актуальности выбранного value:

function syncSelected(slimSelect, options) {
  const selected = slimSelect.selected();

  const exists = options.some(o => o.value === selected);

  if (!exists) {
    slimSelect.set('');
  }
}

Это предотвращает «залипание» несуществующего выбора.

Обработка смешанных источников данных

При объединении локальных и серверных данных часто возникает конфликт форматов. Стратегия унификации:

function mergeSources(local, remote) {
  return uniqueOptions([
    ...sanitizeOptions(local),
    ...sanitizeOptions(remote)
  ]);
}

Ключевой принцип — нормализация до объединения, а не после.

Логирование некорректных элементов

Для сложных систем важно не только удалять ошибки, но и фиксировать их происхождение:

function debugSanitize(options) {
  return (options || []).filter(item => {
    const valid = item && item.text && item.value;

    if (!valid) {
      console.warn('Invalid option:', item);
    }

    return valid;
  });
}

Такой подход позволяет выявлять проблемы на уровне API, а не UI.