Динамические формы в связке с Slim Select предполагают работу с
состоянием DOM, которое изменяется во времени: добавляются новые поля
выбора, удаляются существующие, обновляются списки опций, пересобираются
экземпляры компонентов и синхронизируются значения с моделью данных.
Основная сложность заключается в том, что стандартный
<select> после инициализации библиотеки перестаёт
быть автономной сущностью и требует аккуратного управления жизненным
циклом.
Каждый экземпляр Slim Select оборачивает исходный
<select> и создает собственную структуру DOM. При
динамическом изменении формы важно учитывать три состояния:
При добавлении или удалении элементов формы без корректного обновления экземпляра возникает рассинхронизация между DOM и внутренним состоянием библиотеки.
Базовая инициализация:
const slim = new SlimSelect({
select: '#city'
});
После этого взаимодействие с <select> через прямую
модификацию innerHTML или appendChild не
отражается автоматически в интерфейсе Slim Select без обновления
состояния.
При генерации формы на лету создаются новые DOM-узлы, после чего для каждого нового элемента требуется отдельная инициализация.
function addSelect(id) {
const select = document.createElement('select');
select.id = id;
document.querySelector('#form').appendChild(select);
new SlimSelect({
select: `#${id}`,
data: [
{ text: 'A', value: 'a' },
{ text: 'B', value: 'b' }
]
});
}
В такой модели каждый select становится независимым экземпляром, а управление ими распределяется по набору инстансов.
При масштабировании формы появляется необходимость хранения ссылок на созданные экземпляры:
const instances = new Map();
function registerSelect(id) {
instances.set(id, new SlimSelect({
select: `#${id}`
}));
}
Удаление элемента формы требует синхронного уничтожения связанного экземпляра Slim Select. В противном случае остаются «висячие» обработчики событий и DOM-узлы.
function removeSelect(id) {
const instance = instances.get(id);
if (instance) {
instance.destroy();
instances.delete(id);
}
document.getElementById(id)?.remove();
}
Метод destroy() возвращает select в исходное состояние,
удаляя созданную обёртку библиотеки.
В динамических формах часто требуется изменение доступных значений без пересоздания компонента. В таких случаях применяется обновление данных через API экземпляра.
instance.setData([
{ text: 'X', value: 'x' },
{ text: 'Y', value: 'y' }
]);
После вызова setData внутренняя модель синхронизируется
с новым набором опций, при этом выбранное значение может быть сброшено,
если оно отсутствует в новом списке.
Вариант частичного обновления реализуется через манипуляцию исходным
<select>:
const select = document.querySelector('#city');
select.innerHTML = `
<option value="1">One</option>
<option value="2">Two</option>
`;
instance.set();
Метод set() инициирует пересканирование DOM и
синхронизацию состояния.
Динамические формы часто включают цепочки зависимых списков: выбор в одном поле определяет содержимое следующего.
const country = new SlimSelect({ select: '#country' });
const city = new SlimSelect({ select: '#city' });
document.querySelector('#country').addEventListener('change', (e) => {
const value = e.target.value;
const cities = {
kz: [
{ text: 'Karaganda', value: 'karaganda' },
{ text: 'Almaty', value: 'almaty' }
],
ru: [
{ text: 'Moscow', value: 'moscow' },
{ text: 'Kazan', value: 'kazan' }
]
};
city.setData(cities[value] || []);
});
В подобных структурах важна синхронизация состояний: изменение родительского select приводит к полной переинициализации дочернего набора данных.
При динамических формах часто применяется загрузка опций с сервера. Slim Select не выполняет запросы самостоятельно, поэтому слой получения данных отделяется.
const userSelect = new SlimSelect({
select: '#users'
});
async function loadUsers() {
const res = await fetch('/api/users');
const data = await res.json();
userSelect.setData(
data.map(u => ({
text: u.name,
value: u.id
}))
);
}
Асинхронная подмена данных приводит к перерасчету интерфейса без повторной инициализации компонента.
Сложные формы состоят из повторяющихся блоков, содержащих собственные select-элементы. При клонировании таких блоков требуется изоляция идентификаторов и экземпляров.
function cloneBlock(template) {
const clone = template.cloneNode(true);
const select = clone.querySelector('select');
const instance = new SlimSelect({
select
});
instances.set(select.name, instance);
document.querySelector('#container').appendChild(clone);
}
В таких структурах управление становится иерархическим: каждый блок содержит локальный набор компонентов, не пересекающихся с другими.
Динамические формы часто передают данные через FormData,
где Slim Select не требует специальной сериализации, так как управляет
стандартными <select>.
function serialize(form) {
const data = new FormData(form);
return Object.fromEntries(data.entries());
}
При множественном выборе значения автоматически агрегируются в виде
повторяющихся ключей, что сохраняется в стандартной модели
FormData.
В ситуациях, где изменяется структура <select>
(например, переключение multiple, изменение атрибутов),
применяется полный цикл пересоздания:
instance.destroy();
const newInstance = new SlimSelect({
select: '#city',
settings: {
placeholderText: 'Select value'
}
});
Такой подход фиксирует состояние компонента в соответствии с новой конфигурацией DOM.
При большом количестве динамических элементов используется централизованное управление:
const registry = {
items: new Map(),
add(id, instance) {
this.items.set(id, instance);
},
remove(id) {
const inst = this.items.get(id);
if (inst) inst.destroy();
this.items.delete(id);
},
clear() {
this.items.forEach(i => i.destroy());
this.items.clear();
}
};
Подобная структура снижает вероятность утечек памяти и потери синхронизации состояния при массовых изменениях DOM.
В динамических интерфейсах часто требуется обновление данных без сброса пользовательского ввода. В таких случаях используется стратегия частичной синхронизации:
const currentValue = instance.selected();
instance.setData(newData);
instance.set(currentValue);
Такой подход позволяет сохранять непрерывность состояния даже при полной замене набора данных.