Ленивая загрузка в Tom Select применяется для работы с большими наборами данных, когда заранее загружать полный список опций нецелесообразно. Вместо этого данные подгружаются по мере необходимости: при открытии списка, вводе текста в поиск или прокрутке результатов. Такой подход снижает нагрузку на сеть и ускоряет первичную инициализацию компонента.
В основе ленивой загрузки лежит функция load, которая
вызывается библиотекой в моменты, когда требуется получить новые данные.
Обычно это происходит при вводе текста в поле поиска или при достижении
конца текущего набора опций.
Функция получает два ключевых аргумента: строку запроса и callback, который необходимо вызвать после получения данных.
new TomSelect('#select', {
valueField: 'id',
labelField: 'title',
searchField: 'title',
load: function(query, callback) {
if (!query.length) return callback();
fetch(`/api/items?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(json => callback(json))
.catch(() => callback());
}
});
В этом примере загрузка данных происходит только при наличии пользовательского ввода. Пустой запрос игнорируется, что предотвращает лишние обращения к серверу.
Tom Select вызывает load не только при поиске, но и при
открытии списка, если включена соответствующая конфигурация. Это
позволяет реализовать сценарии с предварительной подгрузкой первой
страницы данных.
new TomSelect('#select', {
load: function(query, callback) {
fetch('/api/items?query=' + query)
.then(res => res.json())
.then(callback)
.catch(() => callback());
},
preload: true
});
Параметр preload может принимать значения
true или "focus". В первом случае данные
загружаются сразу при инициализации, во втором — только при фокусе на
поле ввода.
При работе с большими списками данных используется постраничная загрузка. Tom Select не реализует пагинацию автоматически, но предоставляет достаточно гибкий механизм для её построения через передачу параметров запроса.
new TomSelect('#select', {
pageSize: 20,
load: function(query, callback) {
const page = this.currentPage || 1;
fetch(`/api/items?q=${query}&page=${page}`)
.then(res => res.json())
.then(data => {
this.currentPage = page + 1;
callback(data.items);
})
.catch(() => callback());
}
});
Здесь состояние текущей страницы хранится прямо в экземпляре компонента. При необходимости можно сбрасывать его при изменении запроса пользователя.
Ленивая загрузка тесно связана с серверным поиском, где фильтрация данных происходит на backend-уровне. Это особенно важно при работе с тысячами или миллионами записей.
new TomSelect('#select', {
valueField: 'id',
labelField: 'name',
searchField: ['name', 'description'],
load: function(query, callback) {
const params = new URLSearchParams({
search: query
});
fetch('/api/search?' + params.toString())
.then(r => r.json())
.then(data => callback(data.results))
.catch(() => callback());
}
});
Сервер возвращает уже отфильтрованный набор данных, что позволяет клиенту не выполнять лишнюю обработку.
При частых запросах к одному и тому же набору данных используется кэширование, чтобы уменьшить количество сетевых вызовов. Tom Select не навязывает конкретную стратегию, поэтому кэш реализуется вручную.
const cache = {};
new TomSelect('#select', {
load: function(query, callback) {
if (cache[query]) {
callback(cache[query]);
return;
}
fetch('/api/items?q=' + query)
.then(res => res.json())
.then(data => {
cache[query] = data;
callback(data);
})
.catch(() => callback());
}
});
Такой подход особенно эффективен при повторяющихся поисковых запросах, например при редактировании фильтров.
При вводе текста в поле поиска может возникать большое количество
запросов к серверу. Для оптимизации применяется debounce-логика, которая
ограничивает частоту вызова load.
function debounce(fn, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), delay);
};
}
new TomSelect('#select', {
load: debounce(function(query, callback) {
fetch('/api/items?q=' + query)
.then(r => r.json())
.then(callback)
.catch(() => callback());
}, 300)
});
Задержка позволяет дождаться завершения ввода перед отправкой запроса, снижая нагрузку на сервер.
В некоторых сценариях необходимо не заменять текущие элементы, а дополнять их новыми. Это важно для бесконечной прокрутки.
new TomSelect('#select', {
load: function(query, callback) {
fetch(`/api/items?q=${query}`)
.then(r => r.json())
.then(data => {
const existing = this.options;
Object.keys(data).forEach(key => {
existing[key] = data[key];
});
callback(data);
})
.catch(() => callback());
}
});
Здесь новые элементы добавляются к уже существующим, расширяя доступный список без его сброса.
Во время выполнения запроса можно отслеживать состояние загрузки через встроенные события. Это позволяет интегрировать индикаторы загрузки или блокировать взаимодействие.
const select = new TomSelect('#select', {
load: function(query, callback) {
this.isLoading = true;
fetch('/api/items?q=' + query)
.then(r => r.json())
.then(data => {
this.isLoading = false;
callback(data);
})
.catch(() => {
this.isLoading = false;
callback();
});
}
});
Состояние загрузки может использоваться для визуальной индикации в интерфейсе, особенно при медленных сетевых запросах.
При ленивой загрузке важно корректно обрабатывать ситуации, когда сервер не возвращает данные или запрос завершается ошибкой. Callback всегда должен вызываться, даже при отсутствии результатов.
load: function(query, callback) {
fetch('/api/items?q=' + query)
.then(res => res.ok ? res.json() : [])
.then(data => callback(data))
.catch(() => callback([]));
}
Такой подход предотвращает зависание компонента и сохраняет стабильность интерфейса.
Ленивая загрузка тесно связана с механизмом поиска внутри Tom Select. Каждое изменение строки ввода может интерпретироваться как новый запрос, что позволяет реализовать сложные фильтрационные сценарии с минимальной нагрузкой на клиентскую часть.
При правильной организации серверного API становится возможным разделение ответственности: клиент отвечает только за отображение и управление состоянием, сервер — за выборку и фильтрацию данных.