AJAX-загрузка данных в Tom Select строится вокруг механизма
динамического запроса данных по мере ввода пользователя. Ключевой точкой
интеграции выступает параметр load, который определяет
функцию получения данных с удалённого источника и преобразование ответа
в формат, понятный компоненту.
Функция load вызывается при вводе текста в поле поиска.
Она принимает два аргумента: строку запроса и callback-функцию для
возврата результатов.
new TomSelect('#select', {
valueField: 'id',
labelField: 'title',
searchField: 'title',
load: function(query, callback) {
fetch(`/api/items?q=${encodeURIComponent(query)}`)
.then(res => res.json())
.then(data => {
callback(data.items);
})
.catch(() => {
callback();
});
}
});
Структура ответа должна быть приведена к массиву объектов, где каждый
объект содержит как минимум поля, соответствующие
valueField и labelField. В противном случае
элементы не будут корректно отображаться или идентифицироваться.
При работе с AJAX критичным становится ограничение количества
запросов. Tom Select предоставляет встроенный механизм задержки через
loadThrottle.
new TomSelect('#select', {
loadThrottle: 300,
load: function(query, callback) {
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(callback);
}
});
Значение loadThrottle задаёт минимальный интервал между
запросами. Это предотвращает перегрузку сервера при быстром вводе
текста.
Дополнительно может использоваться проверка длины запроса:
load: function(query, callback) {
if (query.length < 2) return callback();
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(callback);
}
Поведение первичной загрузки данных регулируется параметром
preload. Возможные значения:
true — загрузка сразу при инициализацииfalse — загрузка только по запросу"focus" — загрузка при фокусе на полеnew TomSelect('#select', {
preload: 'focus',
load: function(query, callback) {
fetch('/api/items')
.then(res => res.json())
.then(callback);
}
});
Использование preload: "focus" часто применяется в
справочниках и больших справочных списках, где начальная выборка уже
полезна пользователю.
В реальных API запросы редко ограничиваются одним параметром поиска. Обычно требуется передача фильтров, пагинации или контекстных данных.
load: function(query, callback) {
const params = new URLSearchParams({
search: query,
limit: 20,
lang: 'ru'
});
fetch(`/api/items?${params.toString()}`)
.then(res => res.json())
.then(data => callback(data.items));
}
Такой подход позволяет интегрировать серверную фильтрацию и уменьшить объём передаваемых данных.
При больших объёмах данных сервер часто возвращает результаты
порциями. Tom Select может работать с пагинацией через расширение логики
load.
load: function(query, callback) {
fetch(`/api/items?q=${query}&page=1`)
.then(res => res.json())
.then(data => {
callback(data.items);
});
}
Более сложная реализация включает хранение состояния страницы и подгрузку дополнительных данных при прокрутке списка. В этом случае управление страницами выносится в замыкание или внешний стор.
Часто API возвращает данные в формате, отличающемся от ожидаемого. В таких случаях используется промежуточное преобразование.
load: function(query, callback) {
fetch(`/api/users?search=${query}`)
.then(res => res.json())
.then(data => {
const items = data.results.map(user => ({
id: user.user_id,
title: user.full_name
}));
callback(items);
});
}
Такая нормализация позволяет изолировать компонент от структуры backend API.
При работе с сетью важно учитывать возможные сбои. Tom Select ожидает вызов callback даже в случае ошибки, иначе интерфейс может остаться в состоянии загрузки.
load: function(query, callback) {
fetch(`/api/search?q=${query}`)
.then(res => {
if (!res.ok) throw new Error('Network error');
return res.json();
})
.then(data => callback(data.items))
.catch(() => {
callback();
});
}
Дополнительно может использоваться fallback-логика с локальными данными или кэшированием предыдущих результатов.
Для снижения количества запросов применяется кэширование. Оно
реализуется вручную внутри load.
const cache = {};
new TomSelect('#select', {
load: function(query, callback) {
if (cache[query]) {
callback(cache[query]);
return;
}
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(data => {
cache[query] = data.items;
callback(data.items);
});
}
});
Такой подход особенно эффективен при повторяющихся запросах и ограниченной серверной производительности.
При быстром вводе текста устаревшие AJAX-запросы могут возвращать
данные позже актуальных. Для предотвращения гонок используется
AbortController.
let controller = null;
new TomSelect('#select', {
load: function(query, callback) {
if (controller) {
controller.abort();
}
controller = new AbortController();
fetch(`/api/search?q=${query}`, {
signal: controller.signal
})
.then(res => res.json())
.then(data => callback(data.items))
.catch(() => callback());
}
});
Это обеспечивает согласованность данных и исключает визуальные артефакты в списке.
При работе с защищёнными API необходимо добавление заголовков авторизации.
load: function(query, callback) {
fetch(`/api/items?q=${query}`, {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
})
.then(res => res.json())
.then(data => callback(data.items));
}
При обновлении токенов часто требуется динамическое формирование
заголовков внутри функции load, а не на уровне
инициализации компонента.
Снижение нагрузки на сервер достигается ограничением минимального количества символов:
new TomSelect('#select', {
shouldLoad: function(query) {
return query.length > 2;
}
});
Комбинация shouldLoad и load позволяет
гибко управлять моментом отправки запроса.
Вместо fetch может использоваться любой HTTP-клиент,
например Axios, что упрощает обработку ошибок и интерсепторов.
load: function(query, callback) {
axios.get('/api/search', {
params: { q: query }
})
.then(res => {
callback(res.data.items);
})
.catch(() => callback());
}
Такая интеграция особенно полезна при сложной инфраструктуре фронтенда.
Эффективная работа AJAX в Tom Select зависит от согласованности клиентской и серверной логики. Сервер должен поддерживать:
Клиентская часть при этом должна оставаться максимально легковесной, выполняя только трансформацию и отображение данных.
Во время выполнения запроса компонент автоматически переходит в состояние загрузки, однако при кастомных реализациях может потребоваться явное управление UX через callback-пустые состояния.
load: function(query, callback) {
if (!query) return callback();
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(data => {
if (!data.items.length) return callback();
callback(data.items);
});
}
Подобный контроль позволяет тонко управлять поведением списка при пустых результатах.
Комбинированный подход включает:
Такая архитектура обеспечивает устойчивую работу компонента при высокой интерактивной нагрузке и больших объёмах данных.