Ленивая загрузка

Ленивая загрузка в 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 становится возможным разделение ответственности: клиент отвечает только за отображение и управление состоянием, сервер — за выборку и фильтрацию данных.