Восстановление состояния формы с инициализированными экземплярами
Choices.js требует учёта того, что библиотека заменяет стандартные
<select> и <input> собственным
управляемым состоянием. Простое восстановление значений через установку
value у DOM-элемента часто не приводит к синхронизации
интерфейса, поэтому требуется работа через API экземпляра и корректная
последовательность инициализации.
Основная задача восстановления — корректная сериализация выбранных значений. В Choices.js данные могут представлять собой:
single select)multiple select)choices или remote data)Наиболее устойчивый способ хранения — преобразование состояния в JSON.
const choices = new Choices('#city-select', {
removeItemButton: true
});
function getState(instance) {
return instance.getValue(true); // возвращает массив значений или одно значение
}
function saveState(instance, key) {
const state = getState(instance);
localStorage.setItem(key, JSON.stringify(state));
}
Метод getValue(true) предпочтителен, так как возвращает
«чистые» значения без DOM-обвязки.
После загрузки страницы восстановление нельзя выполнять до инициализации экземпляра. Любая попытка установить значение до создания объекта Choices приводит к потере синхронизации UI.
const choices = new Choices('#city-select', {
removeItemButton: true
});
function restoreState(instance, key) {
const raw = localStorage.getItem(key);
if (!raw) return;
const values = JSON.parse(raw);
instance.removeActiveItems();
if (Array.isArray(values)) {
values.forEach(value => instance.setChoiceByValue(value));
} else {
instance.setChoiceByValue(values);
}
}
restoreState(choices, 'city-select-state');
Ключевой момент заключается в том, что Choices.js не предоставляет
прямого метода setValue для массива, поэтому восстановление
выполняется поэлементно через setChoiceByValue.
При повторной инициализации или восстановлении важно избегать
дублирования выбранных элементов. Метод removeActiveItems()
очищает текущее состояние экземпляра без разрушения структуры
списка.
choices.removeActiveItems();
Если используется динамическая подгрузка данных, дополнительно может потребоваться:
choices.clearChoices();
choices.setChoices(data, 'value', 'label', true);
Особая сложность возникает при использовании асинхронных источников
(ajax, fetch, remote API). В этом случае
восстановление состояния возможно только после загрузки списка
опций.
const choices = new Choices('#city-select');
fetch('/api/cities')
.then(res => res.json())
.then(data => {
choices.setChoices(data, 'id', 'name', true);
const saved = JSON.parse(localStorage.getItem('city-select-state') || '[]');
saved.forEach(id => {
choices.setChoiceByValue(String(id));
});
});
Здесь критично соблюдение порядка:
Нарушение последовательности приводит к игнорированию восстановления, поскольку Choices.js не находит соответствующие значения в списке.
При работе с формами часто требуется восстановить не только отдельные поля, но и целый набор данных. Choices.js необходимо синхронизировать с сериализацией формы.
function serializeForm(form) {
const data = {};
const formData = new FormData(form);
for (const [key, value] of formData.entries()) {
if (!data[key]) data[key] = [];
data[key].push(value);
}
return data;
}
Однако Choices.js не всегда обновляет <select> в
DOM напрямую, поэтому восстановление должно идти через экземпляры:
function restoreForm(formMap, instances) {
Object.keys(formMap).forEach(name => {
const instance = instances[name];
if (!instance) return;
instance.removeActiveItems();
formMap[name].forEach(value => {
instance.setChoiceByValue(value);
});
});
}
Стандартный form.reset() не восстанавливает состояние
Choices.js, поскольку библиотека хранит собственный UI state.
form.addEventListener('reset', () => {
setTimeout(() => {
choices.removeActiveItems();
}, 0);
});
Использование setTimeout необходимо, поскольку событие
reset срабатывает до завершения обновления
DOM-значений.
Для актуализации данных в реальном времени используется событие
change. Choices.js проксирует его, позволяя фиксировать
изменения без дополнительного polling.
choices.passedElement.element.addEventListener('change', () => {
saveState(choices, 'city-select-state');
});
При мультивыборе это особенно важно, так как порядок добавления элементов может иметь значение.
Если данные представлены объектами, а не строками, требуется хранить
не только value, но и метаданные.
choices.setChoices([
{ value: '1', label: 'Almaty' },
{ value: '2', label: 'Astana' }
], 'value', 'label', true);
При восстановлении достаточно использовать value,
поскольку Choices.js сопоставляет его с уже загруженными объектами.
При изменении списка опций между сохранением и восстановлением возможны расхождения. Например, сохранённое значение может отсутствовать в новом наборе данных.
Подход к обработке:
const availableValues = choices._store.choices.map(c => c.value);
savedValues
.filter(v => availableValues.includes(v))
.forEach(v => choices.setChoiceByValue(v));
Игнорирование этого шага приводит к «тихим» ошибкам восстановления, когда часть состояния теряется без уведомлений.
При использовании SSR (например, с шаблонами или фреймворками) важно синхронизировать начальное состояние до инициализации Choices.js.
<select id="city-select">
<option value="1" selected>Almaty</option>
<option value="2">Astana</option>
</select>
Choices.js при инициализации считывает selected, однако
при динамической гидратации предпочтительнее явная передача
состояния:
const initialValues = JSON.parse(document.getElementById('state').dataset.values);
const choices = new Choices('#city-select');
initialValues.forEach(v => choices.setChoiceByValue(v));
При необходимости полного сброса состояния формы необходимо синхронно очистить и UI, и хранилище.
function clearState(instance, key) {
instance.removeActiveItems();
localStorage.removeItem(key);
}
Несогласованность между DOM и localStorage приводит к повторному восстановлению «устаревших» данных при следующей загрузке.
В формах с несколькими экземплярами Choices.js ключевым становится централизованное управление состоянием. Обычно используется структура:
const instances = {
city: new Choices('#city'),
country: new Choices('#country'),
tags: new Choices('#tags')
};
И соответствующее хранилище:
const state = {
city: ['1'],
country: ['kz'],
tags: ['frontend', 'javascript']
};
Восстановление выполняется детерминированно:
Object.keys(state).forEach(key => {
const instance = instances[key];
if (!instance) return;
instance.removeActiveItems();
state[key].forEach(v => instance.setChoiceByValue(v));
});