Как создать зависимые списки select2.js?

есть два селекта

<select id="category" class="form-control" name="category_id">
   <option value="Уборка и помощь по хозяйству">Уборка и помощь по хозяйству</option>
   <option value="Виртиуальный помощник">Виртиуальный помощник</option>
   <option value="Компьютернная помощь">Компьютернная помощь</option>
</select>

и

<select id="sub_category" class="form-control" name="sub_category_id"></select>

вот так подключаю их к select2

$('#category').select2({ placeholder: "Выберите из списка",allowClear: true,language: "ru",});
$('#sub_category').select2({ placeholder: "Выберите из списка",allowClear: true, language: "ru"});

Пытаюсь найти как сделать зависимые списки при помощи плагина select2. Те надо при выборе категории получать по ajax данные под категорий и вставлять во второй список . Пытался гуглить нет информации. Интересует формат ответа, у куда вставлять данные.


Ответы (2 шт):

Автор решения: Алексей Шиманский

В доках написано: https://select2.org/data-sources/ajax, атрибут data. По ajax запросу возвращаются данные, которые можно записать их в данный атрибут

$('#mySelect2').select2({
  ajax: {
    url: 'https://api.github.com/orgs/select2/repos',
    data: function (params) {
      var query = {
        search: params.term,
        type: 'public'
      }

      // Query parameters will be ?search=[term]&type=public
      return query;
    }
  }
});

Ещё пример

$("#profiles-thread").select2({
    minimumInputLength: 2,
    tags: [],
    ajax: {
        url: URL,
        dataType: 'json',
        type: "GET",
        quietMillis: 50,
        data: function (term) {
            return {
                term: term
            };
        },
        results: function (data) {
            return {
                results: $.map(data, function (item) {
                    return {
                        text: item.completeName,
                        slug: item.slug,
                        id: item.id
                    }
                })
            };
        }
    }
});

Если лень самому делать, то можете воспользоваться готовым классом. Пример: https://codepen.io/ajaxray/full/oBPbQe/

/**
 * A Javascript module to loadeding/refreshing options of a select2 list box using ajax based on selection of another select2 list box.
 * 
 * @url : https://gist.github.com/ajaxray/187e7c9a00666a7ffff52a8a69b8bf31
 * @auther : Anis Uddin Ahmad <[email protected]>
 * 
 * Live demo - https://codepen.io/ajaxray/full/oBPbQe/
 * w: http://ajaxray.com | t: @ajaxray
 */
var Select2Cascade = (function(window, $) {

  function Select2Cascade(parent, child, url, select2Options) {
    var afterActions = [];
    var options = select2Options || {};

    // Register functions to be called after cascading data loading done
    this.then = function(callback) {
      afterActions.push(callback);
      return this;
    };

    parent.select2(select2Options).on("change", function(e) {

      child.prop("disabled", true);

      var _this = this;
      $.getJSON(url.replace(':parentId:', $(this).val()), function(items) {
        var newOptions = '<option value="">-- Select --</option>';
        for (var id in items) {
          newOptions += '<option value="' + id + '">' + items[id] + '</option>';
        }

        child.select2('destroy').html(newOptions).prop("disabled", false)
          .select2(options);

        afterActions.forEach(function(callback) {
          callback(parent, child, items);
        });
      });
    });
  }

  return Select2Cascade;

})(window, $);

$(document).ready(function() {
  var select2Options = {
    width: 'resolve'
  };
  // Loading raw JSON files of a secret gist - https://gist.github.com/ajaxray/32c5a57fafc3f6bc4c430153d66a55f5
  var apiUrl = 'https://gist.githubusercontent.com/ajaxray/32c5a57fafc3f6bc4c430153d66a55f5/raw/260a653e6347fb6d2360e8ec376a2dc4888c1afa/:parentId:.json';

  $('select').select2(select2Options);
  var cascadLoading = new Select2Cascade($('#type'), $('#subtype'), apiUrl, select2Options);
  cascadLoading.then(function(parent, child, items) {
    // Dump response data
    console.log(items);
  });
});
/* Space out content a bit */

body {
  padding-top: 20px;
  padding-bottom: 20px;
}


/* Everything but the jumbotron gets side spacing for mobile first views */

.header,
.marketing,
.footer {
  padding-right: 15px;
  padding-left: 15px;
}


/* Custom page header */

.header {
  padding-bottom: 20px;
  border-bottom: 1px solid #e5e5e5;
}


/* Make the masthead heading the same height as the navigation */

.header h3 {
  margin-top: 0;
  margin-bottom: 0;
  line-height: 40px;
}


/* Custom page footer */

.footer {
  padding-top: 19px;
  color: #777;
  border-top: 1px solid #e5e5e5;
}


/* Customize container */

@media (min-width: 768px) {
  .container {
    max-width: 730px;
  }
}

.container-narrow>hr {
  margin: 30px 0;
}


/* Supporting marketing content */

.marketing {
  margin: 40px 0;
}

.marketing p+h4 {
  margin-top: 28px;
}


/* Responsive: Portrait tablets and up */

@media screen and (min-width: 768px) {
  /* Remove the padding we set earlier */
  .header,
  .marketing,
  .footer {
    padding-right: 0;
    padding-left: 0;
  }
  /* Space out the masthead */
  .header {
    margin-bottom: 30px;
  }
  /* Remove the bottom border on the jumbotron for visual effect */
  .jumbotron {
    border-bottom: 0;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>

<div class="container">

  <div class="row">
    <div class="col-sm-12">

      <form class="form-horizontal">
        <div class="form-group">
          <label for="type" class="col-sm-5 control-label">I'd like to ride</label>
          <div class="col-sm-5">
            <select name="type" id="type" class="form-control">
              <option>--Select your ride--</option>
              <option value="animals">Animal</option>
              <option value="vehicles">Vehicle</option>
            </select>
          </div>
        </div>
        <div class="form-group">
          <label for="subtype" class="col-sm-5 control-label">More specifically</label>
          <div class="col-sm-5">
            <select name="subtype" id="subtype" class="form-control">
              <option>-- Select type first--</option>
            </select>
          </div>
        </div>
      </form>

    </div>
  </div>

</div>
<!-- /container -->

→ Ссылка
Автор решения: Макс

Зависимые списки на JavaScript любой вложенности

// <!-- Добавляем контейнер для корневого списка -->
// <div id="rootSelectContainer"></div>

// Исходный массив данных
const select_data = [ /* Объявление массива данных */
    { id: 1, parent_id: 0, action_name: 'Выберите категорию', action_template: '' }, /* Элемент массива данных */
    { id: 2, parent_id: 0, action_name: 'Категория 1', action_template: '' }, /* Элемент массива данных */
    { id: 3, parent_id: 0, action_name: 'Категория 2', action_template: '' },
    { id: 4, parent_id: 0, action_name: 'Категория 3', action_template: '' },

    { id: 5, parent_id: 2, action_name: 'Категория 1-1', action_template: '' },
    { id: 6, parent_id: 2, action_name: 'Категория 1-2', action_template: '' },
    { id: 7, parent_id: 2, action_name: 'Категория 1-3', action_template: '' },

    { id: 8, parent_id: 3, action_name: 'Категория 2-1', action_template: '' },
    { id: 9, parent_id: 3, action_name: 'Категория 2-2', action_template: '' },
    { id: 10, parent_id: 3, action_name: 'Категория 2-3', action_template: '' },

    { id: 11, parent_id: 4, action_name: 'Категория 3-1', action_template: '' },
    { id: 12, parent_id: 4, action_name: 'Категория 3-2', action_template: '' },
    { id: 13, parent_id: 4, action_name: 'Категория 3-3', action_template: '' },

];
//########################################################//
// Функция для создания древовидной структуры
function createTree(arr, parentId) { /* Объявление функции createTree с параметрами arr и parentId */
    let tree = []; /* Инициализация пустого массива tree */
    for (let i = 0; i < arr.length; i++) { /* Цикл по элементам массива arr */
        if (arr[i].parent_id === parentId) { /* Условие: если parent_id элемента равен parentId */
            let children = createTree(arr, arr[i].id); /* Рекурсивный вызов функции createTree для поиска дочерних элементов */
            if (children.length) { /* Если есть дочерние элементы */
                arr[i].children = children; /* Установка свойства children для текущего элемента */
            }
            tree.push(arr[i]); /* Добавление текущего элемента в массив tree */
        }
    }

    return tree; /* Возврат результата - массива древовидной структуры */
}
//########################################################//
// Функция для создания и заполнения select с атрибутами data
function renderSelectOptions(tree, parentNode) { /* Объявление функции renderSelectOptions с параметрами tree и parentNode */
    const select = document.createElement('select'); /* Создание элемента <select> */
    select.onchange = function() { /* Установка обработчика события onchange для элемента select */
        const selectedOption = this.value; /* Получение значения выбранной опции */
        
        // Удаляем все следующие select элементы
        let nextSibling = this.nextSibling; /* Получение следующего соседа элемента select */
        while (nextSibling) { /* Цикл: пока есть следующий сосед */
            parentNode.removeChild(nextSibling); /* Удаление следующего соседа от родительского узла */
            nextSibling = this.nextSibling; /* Получение следующего соседа после удаления */
        }
        
        const selectedNode = tree.find(node => node.id == selectedOption); /* Поиск выбранного узла в дереве */
        if (selectedNode && selectedNode.children) { /* Если выбранный узел и имеет дочерние элементы */
            renderSelectOptions(selectedNode.children, parentNode); /* Рекурсивный вызов функции renderSelectOptions для отображения дочерних элементов */
        }
    };
    tree.forEach(node => { /* Цикл по элементам дерева */
        const option = document.createElement('option'); /* Создание элемента <option> */
        option.value = node.id; /* Установка значения для опции */
        option.textContent = node.action_name; /* Установка текста для опции */
        option.setAttribute('data-parent_id', node.parent_id); /* Установка пользовательского атрибута data-parent_id */
        option.setAttribute('data-action_template', node.action_template); /* Установка пользовательского атрибута data-action_template */
        select.appendChild(option); /* Добавление опции в элемент select */
    });
    parentNode.appendChild(document.createElement('br')); // Добавляем перевод строки /* Добавление перевода строки после элемента select */
    parentNode.appendChild(select); /* Добавление элемента select в родительский узел */

    // Рекурсивно создаем дочерние элементы select
    if (tree.length > 0 && tree[0].children) { /* Условие: если длина дерева больше нуля и первый элемент дерева имеет дочерние элементы */
        renderSelectOptions(tree[0].children, parentNode); /* Рекурсивный вызов функции renderSelectOptions для отображения дочерних элементов */
    }
}
//########################################################//
// Находим корневой контейнер для списка
const rootSelectContainer = document.getElementById('rootSelectContainer'); /* Получение элемента с идентификатором rootSelectContainer */
// Отображаем корневой список с атрибутами data
renderSelectOptions(createTree(select_data, 0), rootSelectContainer); /* Вызов функции renderSelectOptions для отображения корневого списка */
→ Ссылка