FormData представляет собой встроенный механизм браузера
для формирования и передачи данных формы в виде пар ключ–значение.
Объект ориентирован на сценарии отправки данных через fetch
или XMLHttpRequest без необходимости ручной сериализации в
JSON.
Основные особенности:
name → valueFile,
Blob)multipart/form-dataБазовая инициализация:
const form = document.querySelector('form');
const formData = new FormData(form);
Добавление значений вручную:
const formData = new FormData();
formData.append('username', 'alex');
formData.append('role', 'admin');
Извлечение данных:
formData.get('username');
formData.getAll('role');
Библиотека Slim Select заменяет стандартный
<select> кастомным UI-компонентом, сохраняя при этом
исходный элемент в DOM. Это создаёт ключевую особенность: визуальное
состояние отделено от нативной формы.
Slim Select работает поверх <select>, но
фактическая отправка данных через FormData зависит от
синхронизации состояния компонента с DOM-элементом.
Основная проблема:
FormData читает только DOM
<select>Slim Select предоставляет API для доступа к выбранным значениям:
const select = new SlimSelect({
select: '#mySelect'
});
const values = select.getSelected();
Возвращаемые данные:
Пример:
["js", "ts", "node"]
Однако FormData сам по себе не знает о Slim Select,
поэтому необходимо обеспечить актуальность
<select>.
<select>Slim Select автоматически обновляет оригинальный
<select>, устанавливая selected у
<option>.
Пример структуры:
<select id="mySelect" name="skills" multiple>
<option value="js">JavaScript</option>
<option value="ts">TypeScript</option>
<option value="node">Node.js</option>
</select>
После выбора:
<option value="js" selected></option>
<option value="ts" selected></option>
Следовательно:
const formData = new FormData(form);
корректно захватывает значения без дополнительной логики.
При multiple HTML-элементе FormData
автоматически создаёт несколько записей с одинаковым ключом.
Пример:
<select name="skills" multiple>
const formData = new FormData(form);
Результат внутри FormData:
Извлечение:
formData.getAll('skills');
// ["js", "ts", "node"]
Это поведение критично для Slim Select, поскольку он чаще всего используется именно для multi-select интерфейсов.
В некоторых сценариях требуется игнорировать DOM и формировать данные напрямую из API Slim Select.
const select = new SlimSelect({
select: '#mySelect'
});
const formData = new FormData();
const values = select.getSelected();
values.forEach(value => {
formData.append('skills', value);
});
Такой подход применяется, когда:
Альтернативная архитектура основана на hidden input, который синхронизируется со Slim Select.
<input type="hidden" name="skills" id="skillsHidden">
const select = new SlimSelect({
select: '#mySelect',
onChange: (info) => {
const hidden = document.querySelector('#skillsHidden');
hidden.value = info.map(i => i.value).join(',');
}
});
В этом случае FormData читает одно поле:
const formData = new FormData(form);
formData.get('skills'); // "js,ts,node"
Недостаток подхода — потеря нативной структуры multi-value.
FormData не ограничивает хранение строк. Часто применяется сериализация сложных объектов.
const values = select.getSelected();
const formData = new FormData();
formData.append('skills', JSON.stringify(values));
Пример структуры:
[
{ "text": "JavaScript", "value": "js" },
{ "text": "TypeScript", "value": "ts" }
]
На сервере выполняется:
JSON.parse(formData.get('skills'));
Стандартный сценарий интеграции Slim Select с FormData:
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
await fetch('/api/submit', {
method: 'POST',
body: formData
});
});
Особенности:
Content-Type не устанавливается вручнуюmultipart/form-dataВ сложных интерфейсах FormData формируется не только при submit, но и в процессе работы формы.
const select = new SlimSelect({
select: '#mySelect',
onChange: () => {
const formData = buildFormData();
console.log([...formData.entries()]);
}
});
function buildFormData() {
const formData = new FormData();
const values = select.getSelected();
values.forEach(v => formData.append('skills', v));
return formData;
}
Такой подход применяется в:
FormData не имеет метода прямого удаления по индексу массива значений, поэтому пересборка является стандартным решением:
function rebuild(values) {
const fd = new FormData();
values.forEach(v => fd.append('skills', v));
return fd;
}
Slim Select упрощает это через:
select.setSelected([]);
Slim Select может возвращать:
FormData же всегда работает с примитивными значениями.
Следствие:
Пример неправильного подхода:
formData.append('skills', { value: 'js' }); // будет "[object Object]"
Корректный вариант:
formData.append('skills', JSON.stringify({ value: 'js' }));
<select>Slim Select создан, но <option> не обновляются
из-за кастомной инициализации.
Результат:
new FormData(form).getAll('skills'); // []
<select id="mySelect"></select>
без <form> приводит к пустому FormData при
стандартной инициализации.
Решение — ручная сборка.
hidden.value = "js,ts,node";
На сервере это уже строка, а не массив, что ломает типизацию данных.
FormData полностью зависит от:
name="skills"
Отсутствие name делает поле невидимым для FormData.
Slim Select может обновляться динамически:
select.setData([
{ text: 'Go', value: 'go' }
]);
FormData будет корректен только после обновления DOM
<select> или повторной синхронизации.
FormData с Slim Select чаще всего используется в:
Пример payload:
skills=js
skills=ts
skills=node
username=alex
Slim Select обеспечивает только UI-уровень, вся ответственность за структуру данных ложится на FormData и HTML-разметку.