Развитие библиотеки Choices.js сопровождалось изменением архитектуры, внутреннего состояния компонентов, набора методов, форматов конфигурации и поведения событий. Между версиями происходили как косметические улучшения, так и серьёзные несовместимые изменения, затрагивающие существующий код.
Переход между версиями требует понимания:
Первые версии Choices.js предоставляли ограниченный набор возможностей:
Конфигурация выглядела относительно компактной:
const choices = new Choices(element, {
searchEnabled: true,
removeItemButton: true
});
Внутренний API был менее структурирован:
С течением времени некоторые параметры были переименованы для унификации API.
Ранние версии могли использовать:
silent: false
Позже поведение логирования и ошибок стало более строгим, а часть параметров перестала влиять на внутренние предупреждения.
В старых версиях:
searchFloor: 1
означало минимальное количество символов перед началом поиска.
Позже логика поиска изменилась:
В некоторых версиях наблюдалось различие между:
searchFloor: 0
и полностью отключённым поиском:
searchEnabled: false
Одно из наиболее значимых изменений связано с безопасностью.
Ранние версии допускали HTML практически без ограничений:
allowHTML: true
Это позволяло вставлять:
choices.setChoices([
{
value: '1',
label: '<strong>Admin</strong>'
}
]);
Поздние версии стали осторожнее относиться к XSS-рискам.
Изменения включали:
В некоторых версиях HTML рендерился иначе даже при одинаковых настройках.
Метод принимал массив:
choices.setChoices([
{ value: '1', label: 'One' },
{ value: '2', label: 'Two' }
]);
Позже появилась возможность асинхронной загрузки:
choices.setChoices(async () => {
const response = await fetch('/api/users');
return response.json();
});
Это стало важным архитектурным изменением.
Современный API:
В некоторых версиях параметры выглядели так:
setChoices(choices, valueKey, labelKey, replaceChoices)
Например:
choices.setChoices(data, 'id', 'name', true);
Позже логика параметров была переработана.
Проблемы старого API:
choices.clearChoices();
мог очищать:
Поздние версии разделили ответственность методов.
Теперь различаются:
Это уменьшило количество побочных эффектов.
Ранние реализации:
choices.removeActiveItems();
удаляли все выбранные элементы.
Позже появились дополнительные параметры:
choices.removeActiveItemsByValue('admin');
Это повысило точность работы API.
Старые версии генерировали ограниченный набор событий:
element.addEventListener('addItem', event => {
console.log(event.detail);
});
Позже структура события стала значительно богаче.
Современные события могут содержать:
{
id,
value,
label,
customProperties,
groupValue,
keyCode
}
В ранних версиях:
event.detail.value
мог быть единственным полезным полем.
Позже появились:
Со временем были добавлены:
element.addEventListener('search', event => {
console.log(event.detail.value);
});
element.addEventListener('choice', event => {
console.log(event.detail.choice);
});
element.addEventListener('showDropdown', () => {});
Одной из сложностей миграции стало изменение последовательности вызовов.
Например:
choice → addItem → change
choice → change → addItem
Это влияло на:
Ранние реализации использовали относительно простой state container.
Недостатки:
Поздние версии внедрили более централизованное управление состоянием.
Особенности:
Раньше:
choices._store.choices
мог быть обычным массивом.
Позже структура усложнилась:
choices._store.activeChoices
choices._store.activeItems
choices._store.groups
Использование внутренних свойств стало более рискованным.
Ранние версии:
Это создавало проблемы:
Новые версии:
Некоторые ранние версии использовали:
.is-open
.is-selected
.is-highlighted
Позже появились:
.choices__item--selectable
.choices__item--disabled
.choices__list--dropdown
Изменения затрагивали:
Ранние версии:
callbackOnCreateTemplates: function(template) {
return {};
}
Позже шаблоны получили более сложный API:
callbackOnCreateTemplates(strToEl) {
return {
item: ({ classNames }, data) => {
return strToEl(`
<div class="${classNames.item}">
${data.label}
</div>
`);
}
};
}
Изменились:
Старые шаблоны часто ломались после обновления.
Старые версии активно поддерживали:
Из-за этого:
Поздние релизы отказались от legacy browser поддержки.
Последствия:
Ранние версии часто подключались через:
<script src="choices.min.js"></script>
Глобальная переменная:
window.Choices
Поздние версии:
import Choices fr om 'choices.js';
Поддержка:
Ранние версии не предоставляли официальных типов.
Использовались:
Позже библиотека улучшила TypeScript integration.
Появились:
import Choices fr om 'choices.js';
const choices = new Choices(element, {
removeItemButton: true
});
С типами:
Ранние версии использовали относительно простой поиск:
Позднее появилась интеграция с более сложным поиском.
Преимущества:
Ранние версии могли игнорировать некоторые поля:
searchFields: ['label']
Позже поддержка customProperties стала стабильнее:
searchFields: ['label', 'value', 'customProperties.description']
Ранние версии плохо работали с большими наборами данных:
1000+ items
вызывали:
Новые версии:
choices.destroy();
не всегда:
Современные версии корректнее:
Ранние версии позволяли использовать:
choices.containerOuter
choices.input
choices.dropdown
Позже часть свойств:
Некоторые callback-механизмы были заменены событиями.
Например:
callbackOnInit: function() {}
Использование:
element.addEventListener(...)
и внешней логики инициализации.
Ранние версии конфликтовали с:
Причины:
Новые версии:
После обновления часто ломаются:
Обновление версии может нарушить:
Критичная проблема:
change
может вызываться иначе, чем в предыдущих версиях.
Особенно это влияет на:
Полезно выносить настройки:
const choicesConfig = {
searchEnabled: true,
removeItemButton: true
};
Для крупных проектов используется abstraction layer:
class SelectManager {
constructor(element) {
this.instance = new Choices(element);
}
clear() {
this.instance.clearStore();
}
}
Это уменьшает зависимость от внутренних изменений библиотеки.
Нежелательно использовать:
choices._store
choices._currentState
choices._templates
Внутренние структуры меняются между версиями без гарантий совместимости.
Ранние версии Choices.js ориентировались на:
Современные версии превратились в более сложную систему:
Эволюция API привела к увеличению гибкости, но одновременно повысила требования к миграции, тестированию и контролю совместимости между версиями библиотеки.