Обновите данные select2 без перестройки элемента управления

Я преобразовываю <input type="hidden"> в раскрывающийся список select2 и передаю ему данные через метод запроса

$('#inputhidden').select2({
    query: function( query ) {
        query.callback( data ); // the data is in the format select2 expects and all works well..
    );
});

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

введите здесь описание изображения

Теперь мне нужно, чтобы эти обновления происходили без полной перестройки select2, а просто обновляли элементы в раскрывающемся списке. Я не могу найти способ передать новый набор данных в уже созданный элемент управления select2, возможно ли это?


person mfreitas    schedule 10.05.2013    source источник


Ответы (8)


выберите2 v3.x

Если у вас есть локальный массив с параметрами (полученный вызовом ajax), я думаю, вы должны использовать параметр data в качестве функции, возвращающей результаты для поля выбора:

var pills = [{id:0, text: "red"}, {id:1, text: "blue"}]; 

$('#selectpill').select2({
    placeholder: "Select a pill",
    data: function() { return {results: pills}; }
});

$('#uppercase').click(function() {
    $.each(pills, function(idx, val) {
        pills[idx].text = val.text.toUpperCase();
    });
});
$('#newresults').click(function() {
    pills = [{id:0, text: "white"}, {id:1, text: "black"}];
});

FIDDLE: http://jsfiddle.net/RVnfn/576/

В случае, если вы настраиваете интерфейс select2 с помощью кнопок, просто вызовите метод updateResults (этому методу нельзя вызывать из-за пределов объекта select2, но вы можете добавить его в массив allowedMethods в select2, если вам нужно) метод после обновления массива данных (например, таблетки) .


select2 v4: пользовательский адаптер данных

Пользовательский адаптер данных с дополнительным методом updateOptions (непонятно, почему оригинальный ArrayAdapter не имеет этой функциональности) может использоваться для динамического обновления списка опций (все опции в этом примере):

$.fn.select2.amd.define('select2/data/customAdapter',
    ['select2/data/array', 'select2/utils'],
    function (ArrayAdapter, Utils) {
        function CustomDataAdapter ($element, options) {
            CustomDataAdapter.__super__.constructor.call(this, $element, options);
        }
        Utils.Extend(CustomDataAdapter, ArrayAdapter);
        CustomDataAdapter.prototype.updateOptions = function (data) {
            this.$element.find('option').remove(); // remove all options
            this.addOptions(this.convertToOptions(data));
        }        
        return CustomDataAdapter;
    }
);

var customAdapter = $.fn.select2.amd.require('select2/data/customAdapter');

var sel = $('select').select2({
    dataAdapter: customAdapter,
    data: pills
});

$('#uppercase').click(function() {
    $.each(pills, function(idx, val) {
        pills[idx].text = val.text.toUpperCase();
    });
    sel.data('select2').dataAdapter.updateOptions(pills);
});

FIDDLE: https://jsfiddle.net/xu48n36c/1/


select2 v4: транспортная функция ajax

в v4 вы можете определить собственный метод транспорта, который может работать с локальным массивом данных (например, спасибо @Caleb_Kiage, я играл с ним без успеха)

документы: https://select2.github.io/options.html#can-an-ajax-plugin-other-than-jqueryajax-be-used

Select2 использует транспортный метод, определенный в ajax.transport, для отправки запросов к вашему API. По умолчанию этим транспортным методом является jQuery.ajax, но его можно изменить.

$('select').select2({
    ajax: {
        transport: function(params, success, failure) {
            var items = pills;
            // fitering if params.data.q available
            if (params.data && params.data.q) {
                items = items.filter(function(item) {
                    return new RegExp(params.data.q).test(item.text);
                });
            }
            var promise = new Promise(function(resolve, reject) {
                resolve({results: items});
            });
            promise.then(success);
            promise.catch(failure);
        }
    }
});

НО с помощью этого метода вам нужно изменить идентификаторы опций, если текст опции в массиве изменяется - внутренний список элементов опции select2 dom не изменился. Если идентификатор опции в массиве останется прежним - будет отображаться предыдущая сохраненная опция, а не обновленная из массива! Это не проблема, если массив изменяется только путем добавления в него новых элементов - подходит для большинства распространенных случаев.

FIDDLE: https://jsfiddle.net/xu48n36c/3/

person ndpu    schedule 27.06.2013

Я думаю, что достаточно передать данные напрямую:

$("#inputhidden").select2("data", data, true);

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

Спасибо @Bergi за помощь с это.


Если это не обновляется автоматически, вы можете либо попробовать напрямую вызвать его метод updateResults.

$("#inputhidden").select2("updateResults");

Или активируйте его косвенно, отправив триггер на «input.select2-input» следующим образом:

var search = $("#inputhidden input.select2-input");
search.trigger("input");
person zsawyer    schedule 03.06.2013
comment
@AnuradhaJayathilaka Он определяется с помощью select2 здесь - person zsawyer; 09.11.2013
comment
Это странно. Метода нет в документе, и когда я его запустил, он выдал ошибку. - person astroanu; 09.11.2013
comment
@AnuradhaJayathilaka Решение этого может быть в ответе @ndpu о allowedMethods - person zsawyer; 12.11.2013
comment
search.trigger("change") тоже работает хорошо. - person Paul Richter; 04.05.2015

Используя Select2 4.0 с Meteor, вы можете сделать что-то вроде этого:

Template.TemplateName.rendered = ->

  $("#select2-el").select2({
    data  : Session.get("select2Data")
  })

  @autorun ->

    # Clear the existing list options. 
    $("#select2-el").select2().empty()

    # Re-create with new options.
    $("#select2-el").select2({
      data  : Session.get("select2Data")
    })

Что творится:

  1. Когда шаблон отображается...
  2. Инициируйте элемент управления select2 с данными из сеанса.
  3. Функция @autorun (this.autorun) запускается каждый раз, когда изменяется значение Session.get("select2Data").
  4. Всякий раз, когда сеанс изменяется, очищайте существующие параметры select2 и заново создавайте с новыми данными.

Это работает для любого реактивного источника данных, такого как Collection.find().fetch(), а не только для Session.get().

ПРИМЕЧАНИЕ. Начиная с Select2 версии 4.0, вы должны удалить существующие параметры, прежде чем добавлять новые. Подробности см. в этом выпуске GitHub. Нет способа «обновить параметры» без очистки существующих.

Вышеупомянутый кофескрипт. Очень похоже на Javascript.

person Lucidity    schedule 19.10.2015
comment
empty()-ing first был единственным способом заставить его работать, но он также удаляет мои дополнительные параметры конструктора, когда я его повторно инициализирую. - person haferje; 25.11.2015
comment
В вопросе не было упоминания о метеоре. - person Craicerjack; 26.01.2016
comment
@Craicerjack правда, однако я приземлился здесь в поисках решения, которое будет работать с Meteor. Я следовал ответам выше и адаптировался для Meteor. Может быть, сюда приземлятся и другие. - person Lucidity; 26.01.2016
comment
В 4.0 этот ответ сбрасывает управление, чего в первую очередь пытался избежать ОП. - person Anderson Fortaleza; 02.03.2016
comment
Зависит от того, что вы подразумеваете под сбросом управления. ОП написал, что мне нужно, чтобы эти обновления происходили без полной перестройки select2, а просто обновляли элементы в раскрывающемся списке. Это решение должно сделать это. См. упомянутую проблему GitHub — я считаю, что это лучшее, что вы можете сделать в 4.0. Не стесняйтесь редактировать, если вы можете сделать лучше. - person Lucidity; 02.03.2016
comment
Очень полезный совет по поводу отсутствия способа «обновить параметры» - спасибо! - person John Zabroski; 14.06.2016
comment
@DataMeister Вам лучше создать новый вопрос и ответить на него своим собственным соответствующим решением, поэтому, когда кто-то будет его искать, он найдет ответ на то, что искал. - person adamj; 27.03.2017

Попробуй это:

var data = [{id: 1, text: 'First'}, {id: 2, text: 'Second'}, {...}];
$('select[name="my_select"]').empty().select2({
    data: data
});
person SpinyMan    schedule 29.01.2019
comment
Этот код ясен для понимания: когда мы очищаем окно выбора, select2 обновляется, а когда мы добавляем новые параметры, данные select2 обновляются снова. Таким образом, нет необходимости запускать change и change.select2. - person SpinyMan; 29.01.2019
comment
Это перестраивает select2, что означает, что если у вас были какие-либо конфигурации, они будут потеряны. Если у вас были прикреплены какие-либо события, вам нужно сначала отсоединить их, а затем снова прикрепить. - person Diego Jancic; 09.07.2020

Я решил эту проблему, используя опцию ajax и указав пользовательскую транспортную функцию.

см. эту скрипту демонстрация динамических опций Select2

Вот соответствующий js, чтобы заставить это работать.

var $items = [];

let options = {
ajax: {
    transport: function(params, success, failure) {
    let items = $items;

    if (params.data && params.data.q) {
        items = items.filter(function(item) {
        return new RegExp(params.data.q).test(item.text);
        });
    }

    let promise = new Promise(function(resolve, reject) {
        resolve({
        results: items
        });
    });

    promise.then(success);
    promise.catch(failure);
    }
},
placeholder: 'Select item'
};

$('select').select2(options);

let count = $items.length + 1;

$('button').on('click', function() {
$items.push({
    id: count,
    text: 'Item' + count
});
count++;
});
person Caleb Kiage    schedule 30.04.2017

Насколько я могу судить, невозможно обновить параметры select2 без обновления всего списка или ввода текста поиска и использования функции запроса.

Что должны делать эти кнопки? Если они используются для определения параметров выбора, почему бы не поместить их за пределы поля выбора и программно установить данные поля выбора, а затем открыть его? Я не понимаю, почему вы хотите разместить их поверх окна поиска. Если пользователь не должен выполнять поиск, вы можете использовать параметр MinimumResultsForSearch, чтобы скрыть функцию поиска.

Изменить: как насчет этого...

HTML:

<input type="hidden" id="select2" class="select" />

JavaScript

var data = [{id: 0, text: "Zero"}],
    select = $('#select2');

select.select2({
  query: function(query) {
    query.callback({results: data});
  },
  width: '150px'
});

console.log('Opening select2...');
select.select2('open');

setTimeout(function() {
  console.log('Updating data...');
  data = [{id: 1, text: 'One'}];
}, 1500);

setTimeout(function() {
  console.log('Fake keyup-change...');
  select.data().select2.search.trigger('keyup-change');
}, 3000);

Пример: Plunker

Редактировать 2: Это, по крайней мере, заставит его обновить список, однако все еще есть некоторая странность, если вы ввели текст поиска до запуска события keyup-change.

person rtcherry    schedule 12.05.2013
comment
кнопки будут выполнять вызовы ajax в зависимости от того, что выбрано, и причина, по которой они плавают над вводом поиска, заключается в том, что мы не можем позволить себе иметь их снаружи, для них нет места. Пользователь будет использовать поиск, я применил к нему padding-left, чтобы текст начинался после двух кнопок. - person mfreitas; 13.05.2013
comment
Каково содержимое раскрывающегося списка перед нажатием одной из кнопок? Вы можете сделать так, чтобы вызов ajax закрыл раскрывающийся список select2, а затем снова открыл его. Вы также можете заставить его захватывать любой текст поиска, повторно вводить его и активировать нажатие клавиши, чтобы закрытие / открытие выглядело менее резким. - person rtcherry; 13.05.2013
comment
select2 не может закрываться и открываться снова между вызовами, он должен быть создан только один раз, будет сделан вызов для заполнения раскрывающегося списка, после этого каждый раз, когда вы нажимаете Btn A или B, они будут совершать вызов и обновлять только содержимое раскрывающегося списка, что это сложная часть :/ - person mfreitas; 13.05.2013

Для Select2 4.X

var instance = $('#select2Container').data('select2');
var searchVal = instance.dropdown.$search.val();
instance.trigger('query', {term:searchVal});
person Andrey Ganzevich    schedule 25.08.2016

комментарий Диего к ответ, данный SpinyMan, важен, потому что метод empty() удалит экземпляр select2, поэтому любые пользовательские параметры больше не будут сохраняться. Если вы хотите сохранить существующие параметры select2, вы должны сохранить их, уничтожить существующий экземпляр select2, а затем повторно инициализировать. Вы можете сделать это так:

const options = JSON.parse(JSON.stringify(
  $('#inputhidden').data('select2').options.options
));
options.data = data;

$('#inputhidden').empty().select2('destroy').select2(options);

Однако я бы рекомендовал всегда явно передавать параметры select2, потому что вышеприведенное копирует только простые параметры, а не какие-либо пользовательские обратные вызовы или адаптеры. Также обратите внимание, что для этого требуется последняя стабильная версия select2 (4.0.13 на момент написания этой публикации).

Я написал общие функции, чтобы справиться с этим с несколькими функциями:

  • может обрабатывать селекторы, которые возвращают несколько экземпляров
  • использовать существующие параметры экземпляра select2 (по умолчанию) или передать новый набор параметров
  • сохранить любые уже выбранные значения (по умолчанию), которые все еще действительны, или удалить их полностью
function select2UpdateOptions(
  selector,
  data,
  newOptions = null,
  keepExistingSelected = true
) {
  // loop through all instances of the matching selector and update each instance
  $(selector).each(function() {
    select2InstanceUpdateOptions($(this), data, newOptions, keepExistingSelected);
  });
}

// update an existing select2 instance with new data options
function select2InstanceUpdateOptions(
  instance,
  data,
  newOptions = null,
  keepSelected = true
) {
  // make sure this instance has select2 initialized
  // @link https://select2.org/programmatic-control/methods#checking-if-the-plugin-is-initialized
  if (!instance.hasClass('select2-hidden-accessible')) {
    return;
  }

  // get the currently selected options
  const existingSelected = instance.val();

  // by default use the existing options of the select2 instance unless overridden
  // this will not copy over any callbacks or custom adapters however
  const options = (newOptions)
    ? newOptions
    : JSON.parse(JSON.stringify(instance.data('select2').options.options))
  ;

  // set the new data options that will be used
  options.data = data;
      
  // empty the select and destroy the existing select2 instance
  // then re-initialize the select2 instance with the given options and data
  instance.empty().select2('destroy').select2(options);
      
  // by default keep options that were already selected;
  // any that are now invalid will automatically be cleared
  if (keepSelected) {
    instance.val(existingSelected).trigger('change');
  }
}
person Jason Roman    schedule 07.10.2020