Валидация перед отправкой

Работа с кастомными селектами на базе Slim Select требует строгого контроля состояния выбранных значений перед отправкой формы. В отличие от стандартного <select>, библиотека управляет состоянием через JavaScript-объект, поэтому проверка данных должна учитывать не только DOM, но и внутреннее состояние экземпляра Slim Select.

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


Основные источники данных для валидации

Перед отправкой формы необходимо учитывать три уровня состояния:

1. DOM-значение <select> Фактическое значение элемента формы, которое может быть изменено как Slim Select, так и внешним кодом.

2. Внутреннее состояние Slim Select Объект selected внутри экземпляра, который отражает текущий выбор пользователя.

3. Синхронизированное значение формы Итоговое состояние, которое будет отправлено на сервер после сериализации формы.

Несоответствие между этими уровнями часто возникает при программном изменении значений через API библиотеки.


Базовая проверка обязательного поля

Для одиночного выбора достаточно проверить наличие значения:

const select = new SlimSelect({
  select: '#country'
});

function validateCountry() {
  const value = select.selected();

  if (!value) {
    return {
      valid: false,
      message: 'Поле страны обязательно для заполнения'
    };
  }

  return { valid: true };
}

Метод selected() возвращает текущее значение или null, если выбор отсутствует. При использовании стандартного <select multiple> структура результата изменяется.


Валидация множественного выбора

При включенном режиме isMultiple: true результат представляет собой массив значений. В этом случае проверка должна учитывать длину массива:

const select = new SlimSelect({
  select: '#skills',
  isMultiple: true
});

function validateSkills() {
  const values = select.selected();

  if (!Array.isArray(values) || values.length === 0) {
    return {
      valid: false,
      message: 'Необходимо выбрать хотя бы один навык'
    };
  }

  return { valid: true };
}

Особенность заключается в том, что даже при отсутствии выбора Slim Select может возвращать пустую строку или undefined в зависимости от версии и конфигурации, поэтому проверка должна быть устойчивой к типам.


Проверка обязательных зависимых полей

Часто Slim Select используется в связке с зависимыми селектами, где выбор одного поля влияет на доступные опции другого. В таких сценариях валидация должна учитывать порядок заполнения.

const countrySelect = new SlimSelect({ select: '#country' });
const citySelect = new SlimSelect({ select: '#city' });

function validateLocation() {
  const country = countrySelect.selected();
  const city = citySelect.selected();

  if (!country) {
    return {
      valid: false,
      message: 'Сначала необходимо выбрать страну'
    };
  }

  if (!city) {
    return {
      valid: false,
      message: 'Необходимо выбрать город'
    };
  }

  return { valid: true };
}

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


Валидация после программного изменения значений

Slim Select позволяет изменять значение программно через методы set() или обновление данных. В таких случаях важно учитывать, что DOM-состояние может обновляться асинхронно.

select.set(['js', 'nodejs']);

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

Практически это решается через отложенную проверку:

setTimeout(() => {
  validateSkills();
}, 0);

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


Валидация значений с асинхронной загрузкой

При использовании динамической подгрузки данных (например, через API) возможна ситуация, когда пользователь отправляет форму до завершения загрузки опций.

const select = new SlimSelect({
  select: '#users',
  ajax: (search, callback) => {
    fetch(`/api/users?q=${search}`)
      .then(res => res.json())
      .then(data => {
        callback(data);
      });
  }
});

В этом случае необходимо учитывать два состояния:

  • загрузка данных не завершена;
  • выбранное значение может быть временно некорректным.

Дополнительный флаг состояния загрузки становится частью валидации:

let isLoading = false;

const select = new SlimSelect({
  select: '#users',
  ajax: (search, callback) => {
    isLoading = true;

    fetch(`/api/users?q=${search}`)
      .then(res => res.json())
      .then(data => {
        isLoading = false;
        callback(data);
      });
  }
});

function validateUsers() {
  if (isLoading) {
    return {
      valid: false,
      message: 'Дождитесь завершения загрузки данных'
    };
  }

  const value = select.selected();

  if (!value) {
    return {
      valid: false,
      message: 'Необходимо выбрать пользователя'
    };
  }

  return { valid: true };
}

Проверка допустимости значений

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

function validateAgainstOptions(selectInstance) {
  const value = selectInstance.selected();
  const options = selectInstance.data();

  const exists = options.some(option => option.value === value);

  if (!exists) {
    return {
      valid: false,
      message: 'Выбранное значение недопустимо'
    };
  }

  return { valid: true };
}

Для множественного выбора проверка выполняется через перебор массива значений.


Обработка кастомных ошибок Slim Select

В ряде случаев Slim Select может находиться в состоянии неконсистентности после ручного вмешательства в DOM. Например, при удалении <option> без обновления экземпляра библиотеки.

Для предотвращения подобных ситуаций используется принудительная синхронизация перед валидацией:

select.setData(newData);
select.set('');

После этого проверка выполняется на основе обновленного состояния, что исключает рассинхронизацию.


Интеграция с нативной HTML-валидацией

Slim Select не заменяет полностью стандартную валидацию формы, поэтому имеет смысл комбинировать его с атрибутами HTML5.

<select id="country" required>
  <option value="">Выберите страну</option>
</select>

Однако нативная проверка не всегда учитывает кастомное состояние компонента. Поэтому перед отправкой формы необходимо принудительно запускать собственную логику:

form.addEventListener('submit', (e) => {
  e.preventDefault();

  const result = validateCountry();

  if (!result.valid) {
    console.error(result.message);
    return;
  }

  form.submit();
});

Комплексная схема валидации формы

При наличии нескольких Slim Select-инстансов целесообразно централизовать проверку:

const validators = [
  validateCountry,
  validateCity,
  validateSkills
];

function validateForm() {
  for (const validate of validators) {
    const result = validate();

    if (!result.valid) {
      return result;
    }
  }

  return { valid: true };
}

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


Особенности работы с reset-состоянием

После сброса формы через form.reset() Slim Select не всегда автоматически возвращается в исходное состояние. Это создаёт риск отправки устаревших значений.

Для корректной обработки требуется ручной сброс:

form.addEventListener('reset', () => {
  select.set('');
});

В случае множественного выбора:

select.set([]);

Контроль финального состояния перед отправкой

Финальный этап валидации заключается в формировании единого состояния всех селектов и его проверке перед отправкой:

function beforeSubmit() {
  const result = validateForm();

  if (!result.valid) {
    return false;
  }

  return true;
}

form.addEventListener('submit', (e) => {
  if (!beforeSubmit()) {
    e.preventDefault();
  }
});

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