Синхронизация с внешним хранилищем

SortableJS — это легковесная библиотека для реализации drag-and-drop интерфейсов в JavaScript. Одной из ключевых возможностей при работе с динамическими списками является синхронизация изменений с внешними хранилищами данных, такими как серверная база, локальное хранилище браузера или облачные сервисы.

Основные принципы синхронизации

Синхронизация требует отслеживания изменений состояния элементов списка и передачи этих изменений в выбранное хранилище. Ключевые моменты:

  • Отслеживание событий: SortableJS предоставляет набор событий (onEnd, onAdd, onUpdate, onRemove), которые позволяют фиксировать момент завершения перетаскивания элементов.
  • Идентификация элементов: Для корректной синхронизации каждый элемент должен иметь уникальный идентификатор. Это может быть атрибут data-id или свойство объекта в массиве.
  • Минимизация нагрузки на хранилище: Необходимо передавать только изменения, а не весь список, чтобы уменьшить сетевой трафик и нагрузку на сервер.

Использование событий для обновления данных

Основное событие для синхронизации — onEnd. Оно срабатывает после завершения перетаскивания элемента. Пример обработки:

const list = document.getElementById('items');

Sortable.create(list, {
  animation: 150,
  onEnd: function (evt) {
    const itemEl = evt.item;  
    const oldIndex = evt.oldIndex;
    const newIndex = evt.newIndex;
    updateStorage(itemEl.dataset.id, oldIndex, newIndex);
  }
});

function updateStorage(id, oldIndex, newIndex) {
  // пример обновления на сервере
  fetch('/update-item-position', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ id, oldIndex, newIndex })
  });
}

В этом примере происходит:

  • Получение элемента, который перемещался (evt.item).
  • Определение старого и нового индекса.
  • Отправка запроса на сервер для обновления позиции элемента в базе данных.

Синхронизация с массивом в памяти

Если список представлен массивом JavaScript, синхронизация может происходить напрямую:

let items = [
  { id: 1, name: 'Элемент 1' },
  { id: 2, name: 'Элемент 2' },
  { id: 3, name: 'Элемент 3' }
];

Sortable.create(document.getElementById('items'), {
  animation: 150,
  onEnd: function (evt) {
    const [movedItem] = items.splice(evt.oldIndex, 1);
    items.splice(evt.newIndex, 0, movedItem);
  }
});

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

Работа с локальным хранилищем браузера

Для сохранения состояния списка между сессиями можно использовать localStorage:

function saveToLocalStorage(items) {
  localStorage.setItem('sortableItems', JSON.stringify(items));
}

function loadFromLocalStorage() {
  const stored = localStorage.getItem('sortableItems');
  return stored ? JSON.parse(stored) : [];
}

let items = loadFromLocalStorage();

Sortable.create(document.getElementById('items'), {
  animation: 150,
  onEnd: function (evt) {
    const [movedItem] = items.splice(evt.oldIndex, 1);
    items.splice(evt.newIndex, 0, movedItem);
    saveToLocalStorage(items);
  }
});

Использование локального хранилища гарантирует, что порядок элементов сохраняется даже после обновления страницы.

Синхронизация с удалённым сервером и оптимизация

При работе с удалёнными серверами рекомендуется:

  • Делать асинхронные запросы с использованием fetch или Axios.
  • Обрабатывать ошибки и откатывать изменения на клиенте при неудачном сохранении.
  • Пакетная отправка изменений, если пользователь перемещает несколько элементов подряд, чтобы снизить количество запросов.

Пример пакетной отправки:

let changedItems = [];

Sortable.create(document.getElementById('items'), {
  animation: 150,
  onEnd: function (evt) {
    const [movedItem] = items.splice(evt.oldIndex, 1);
    items.splice(evt.newIndex, 0, movedItem);
    changedItems.push({ id: movedItem.id, newIndex: evt.newIndex });
    if (changedItems.length > 5) {
      syncWithServer(changedItems);
      changedItems = [];
    }
  }
});

function syncWithServer(changes) {
  fetch('/sync-items', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(changes)
  });
}

Поддержка нескольких списков

SortableJS поддерживает возможность объединения нескольких списков в одну группу через опцию group. Это позволяет синхронизировать перемещения между списками:

Sortable.create(document.getElementById('list1'), { group: 'shared', animation: 150 });
Sortable.create(document.getElementById('list2'), { group: 'shared', animation: 150 });

При этом важно обрабатывать события onAdd и onRemove для корректного обновления внешнего хранилища:

onAdd: function(evt) {
  const movedItem = evt.item;
  addItemToStorage(evt.to.id, movedItem.dataset.id, evt.newIndex);
},
onRemove: function(evt) {
  removeItemFromStorage(evt.from.id, evt.item.dataset.id);
}

Использование сериализации состояния

Для удобства можно сериализовать весь список в виде JSON, что облегчает интеграцию с внешними хранилищами:

function serializeList(listEl) {
  return Array.from(listEl.children).map(el => el.dataset.id);
}

Сериализованный массив может быть сразу сохранён на сервере или в локальном хранилище.

Заключение по методике синхронизации

  • События SortableJS — основной механизм для отслеживания изменений.
  • Использование уникальных идентификаторов обеспечивает корректную привязку элементов к данным.
  • Минимизация объёма передаваемой информации повышает производительность.
  • Поддержка нескольких списков и пакетная синхронизация позволяют создавать сложные интерфейсы с надёжной интеграцией с внешними хранилищами.