Параметры запроса

При работе с удалёнными источниками данных Tom Select опирается на функцию загрузки, в которой ключевую роль играют параметры запроса. Именно они определяют структуру URL, формат передаваемых данных, поведение поиска на сервере и возможность расширения логики под конкретный API.

Базовый механизм загрузки строится вокруг функции:

load: function(query, callback) {
    fetch(`/api/search?q=${encodeURIComponent(query)}`)
        .then(res => res.json())
        .then(data => callback(data.items))
}

Здесь query — это строка поиска, формируемая компонентом, а callback — функция, принимающая результат. Однако реальная работа с параметрами значительно шире, чем простая передача q.


Параметр запроса по умолчанию и его переопределение

В стандартной конфигурации Tom Select использует параметр запроса q. Он передаётся на сервер как ключ поиска:

/api/search?q=term

Изменение имени параметра выполняется через настройку:

new TomSelect("#select", {
    valueField: "id",
    labelField: "title",
    searchField: "title",
    loadThrottle: 300,
    load: function(query, callback) {
        const url = `/api/search?query=${encodeURIComponent(query)}`;

        fetch(url)
            .then(res => res.json())
            .then(json => callback(json.items))
    }
});

В этом примере параметр q заменён на query. Такое переименование важно при интеграции с API, где используются нестандартные схемы фильтрации.


Формирование сложных query-параметров

В реальных проектах запрос редко ограничивается одной строкой поиска. Чаще требуется передавать дополнительные параметры: тип сущности, язык, статус, фильтры доступа.

Пример расширенного формирования URL:

load: function(query, callback) {
    const params = new URLSearchParams({
        q: query,
        type: "user",
        status: "active",
        limit: 20
    });

    fetch(`/api/search?${params.toString()}`)
        .then(res => res.json())
        .then(json => callback(json.items));
}

Такой подход позволяет централизованно управлять структурой запроса и легко добавлять новые фильтры без изменения логики компонента.


Динамическая генерация параметров

Параметры запроса часто зависят от состояния интерфейса: выбранных фильтров, роли пользователя или контекста формы.

load: function(query, callback) {
    const activeOnly = document.querySelector("#activeOnly").checked;
    const role = document.querySelector("#role").value;

    const params = new URLSearchParams({
        q: query,
        active: activeOnly ? 1 : 0,
        role: role
    });

    fetch(`/api/search?${params.toString()}`)
        .then(res => res.json())
        .then(json => callback(json.items));
}

Здесь запрос становится реактивным относительно внешнего состояния интерфейса. Это часто используется в административных панелях и CRM-системах.


Контроль частоты запросов через loadThrottle

При вводе текста пользователь может генерировать десятки запросов в секунду. Для управления нагрузкой используется параметр:

loadThrottle: 300

Он задаёт задержку в миллисекундах между последовательными запросами. Фактически реализуется поведенческий debounce на уровне компонента.

Пример:

new TomSelect("#select", {
    valueField: "id",
    labelField: "name",
    searchField: "name",
    loadThrottle: 500,
    load: function(query, callback) {
        fetch(`/api/items?q=${encodeURIComponent(query)}`)
            .then(res => res.json())
            .then(json => callback(json.data));
    }
});

При значении 500 запрос отправляется только после паузы в полсекунды, что снижает нагрузку на сервер и предотвращает гонку ответов.


Построение URL через отдельную функцию

При усложнении логики формирования параметров удобно выносить генерацию URL в отдельный слой:

function buildSearchUrl(query, context) {
    const params = new URLSearchParams();

    params.set("q", query);
    params.set("lang", context.lang);
    params.set("tenant", context.tenantId);

    if (context.onlyActive) {
        params.set("active", "1");
    }

    return `/api/search?${params.toString()}`;
}

Использование в Tom Select:

load: function(query, callback) {
    const url = buildSearchUrl(query, {
        lang: "ru",
        tenantId: "crm_1",
        onlyActive: true
    });

    fetch(url)
        .then(res => res.json())
        .then(json => callback(json.items));
}

Такой подход отделяет бизнес-логику формирования запроса от UI-компонента.


Передача параметров через POST-запрос

Некоторые API требуют передачу фильтров в теле запроса. В этом случае используется POST:

load: function(query, callback) {
    fetch("/api/search", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            q: query,
            limit: 30,
            includeArchived: false
        })
    })
    .then(res => res.json())
    .then(json => callback(json.items));
}

POST-формат особенно важен при большом количестве параметров или чувствительных данных, которые нежелательно передавать через URL.


Интеграция с контекстными параметрами формы

Tom Select часто используется внутри форм, где параметры запроса зависят от других полей:

const countrySelect = new TomSelect("#country");
const citySelect = new TomSelect("#city", {
    load: function(query, callback) {
        const countryId = countrySelect.getValue();

        const params = new URLSearchParams({
            q: query,
            country: countryId
        });

        fetch(`/api/cities?${params.toString()}`)
            .then(res => res.json())
            .then(json => callback(json.items));
    }
});

В этом сценарии запрос становится зависимым от другого компонента интерфейса, формируя каскадную фильтрацию.


Параметры пагинации

При больших объёмах данных сервер часто возвращает результаты постранично. В запрос добавляются параметры page и limit:

load: function(query, callback) {
    const params = new URLSearchParams({
        q: query,
        page: 1,
        limit: 50
    });

    fetch(`/api/search?${params.toString()}`)
        .then(res => res.json())
        .then(json => callback(json.items));
}

Более сложная версия учитывает динамическое переключение страниц:

load: function(query, callback) {
    let page = 1;

    const loadPage = () => {
        const params = new URLSearchParams({
            q: query,
            page: page
        });

        fetch(`/api/search?${params}`)
            .then(res => res.json())
            .then(json => {
                callback(json.items);

                if (json.hasMore) {
                    page++;
                }
            });
    };

    loadPage();
}

Контроль параметров через shouldLoad

Не всегда требуется отправлять запрос при любом вводе. Для фильтрации лишних обращений используется:

shouldLoad: function(query) {
    return query.length >= 2;
}

Это ограничивает формирование запросов до тех пор, пока пользователь не введёт минимум символов. В связке с параметрами запроса это снижает количество мусорных обращений:

new TomSelect("#select", {
    shouldLoad: function(query) {
        return query.length > 1;
    },
    load: function(query, callback) {
        const params = new URLSearchParams({
            q: query,
            minScore: 0.5
        });

        fetch(`/api/search?${params}`)
            .then(res => res.json())
            .then(json => callback(json.items));
    }
});

Заголовки и авторизация в запросах

Параметры запроса включают не только query string, но и HTTP-заголовки. Это важно при работе с защищёнными API:

load: function(query, callback) {
    fetch(`/api/search?q=${encodeURIComponent(query)}`, {
        headers: {
            "Authorization": `Bearer ${localStorage.getItem("token")}`,
            "Accept": "application/json"
        }
    })
    .then(res => res.json())
    .then(json => callback(json.items));
}

Заголовки позволяют отделить параметры фильтрации от параметров доступа, сохраняя структуру URL чистой.


Инъекция пользовательских параметров через контекст

Иногда требуется передавать параметры, зависящие от окружения приложения, например multi-tenant системы:

const context = {
    tenant: window.APP_TENANT,
    locale: navigator.language
};

new TomSelect("#select", {
    load: function(query, callback) {
        const params = new URLSearchParams({
            q: query,
            tenant: context.tenant,
            locale: context.locale
        });

        fetch(`/api/search?${params.toString()}`)
            .then(res => res.json())
            .then(json => callback(json.items));
    }
});

Такой механизм позволяет использовать один и тот же компонент в разных окружениях без изменения логики загрузки.


Кэширование параметризованных запросов

При повторяющихся запросах полезно учитывать параметры как часть ключа кэша:

const cache = new Map();

function getCacheKey(query, params) {
    return `${query}:${JSON.stringify(params)}`;
}

load: function(query, callback) {
    const extra = { type: "user" };
    const key = getCacheKey(query, extra);

    if (cache.has(key)) {
        callback(cache.get(key));
        return;
    }

    const urlParams = new URLSearchParams({
        q: query,
        type: extra.type
    });

    fetch(`/api/search?${urlParams}`)
        .then(res => res.json())
        .then(json => {
            cache.set(key, json.items);
            callback(json.items);
        });
}

Кэширование становится зависимым не только от строки запроса, но и от всех параметров, влияющих на результат.


Обобщённая модель параметров запроса

Внутренняя логика формирования запроса в Tom Select может быть представлена как последовательность трансформаций:

  1. Ввод пользователя → query
  2. Проверка shouldLoad
  3. Добавление контекстных параметров
  4. Формирование URL или тела запроса
  5. Отправка через fetch или AJAX
  6. Обработка ответа и передача в callback

Каждый этап может быть расширен или переопределён, что делает систему гибкой для интеграции с любыми backend-архитектурами.