【问题标题】:Update select2 data without rebuilding the control更新 select2 数据而不重建控件
【发布时间】:2013-05-05 01:24:09
【问题描述】:

我正在将 <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 UI 并在搜索栏顶部放置两个按钮,单击该按钮将执行 ajax 调用,并且必须更新 select2 内容。

现在,我需要在不完全重建 select2 的情况下进行这些更新,而只是刷新下拉列表中的项目。我找不到将一组新数据传递给已创建的 select2 控件的方法,这可能吗?

【问题讨论】:

    标签: jquery jquery-select2


    【解决方案1】:

    select2 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"}];
    });
    

    FIDDLEhttp://jsfiddle.net/RVnfn/576/

    如果你自定义了带有按钮的select2界面,只需在更新后调用updateResults(此方法不允许从select2对象外部调用,但如果需要,可以将其添加到select2中的allowedMethods数组中)方法数据数组(以药丸为例)。


    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);
    });
    

    FIDDLEhttps://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);
            }
        }
    });
    

    但是如果数组中的选项文本发生更改,则使用此方法您需要更改选项的 ID - 内部 select2 dom 选项元素列表未修改。如果数组中选项的 id 保持不变 - 将显示以前保存的选项,而不是从数组中更新!如果仅通过向其添加新项目来修改数组,这不是问题 - 对于大多数常见情况都可以。

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

    【讨论】:

    • 检查下面的小提琴..它呈现结果但过滤器不起作用..jsfiddle.net/RVnfn/106
    • @JeevaJsb 您正在使用“检查”键添加对象...搜索需要“文本”键!
    • @ndpu 感谢编辑,现在可以正常使用,我已删除我的评论(5 分钟后无法编辑)
    • @ndpu 你太棒了,这正是我需要的,谢谢!
    【解决方案2】:

    我觉得直接交数据就够了:

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

    请注意,第二个参数似乎表明需要刷新。

    感谢@Bergi 对this 的帮助。


    如果没有自动更新,您可以尝试直接调用它的 updateResults 方法。

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

    或者通过向“input.select2-input”发送触发器来间接触发它,如下所示:

    var search = $("#inputhidden input.select2-input");
    search.trigger("input");
    

    【讨论】:

    • @AnuradhaJayathilaka 定义为by select2 here
    • 这很奇怪。该方法不在文档上,当我运行它时它给出了错误。
    • @AnuradhaJayathilaka 解决方案可能在@ndpu 对allowedMethods 的回答中
    • search.trigger("change") 似乎也运行良好。
    【解决方案3】:

    将 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. 使用来自 Session 的数据初始化一个 select2 控件。
    3. @autorun (this.autorun) 函数在每次 Session.get("select2Data") 的值发生变化时运行。
    4. 每当 Session 发生变化时,清除现有的 select2 选项并使用新数据重新创建。

    这适用于任何响应式数据源 - 例如 Collection.find().fetch() - 而不仅仅是 Session.get()。

    注意:从 Select2 版本 4.0 开始,您必须在添加新选项之前删除现有选项。 See this GitHub Issue for details。在不清除现有选项的情况下,没有“更新选项”的方法。

    以上是coffeescript。 Javascript 非常相似。

    【讨论】:

    • empty()-ing first 是我可以让它工作的唯一方法,但是当我重新初始化它时它也会删除我的额外构造函数选项。
    • 问题中没有提到流星。
    • @Craicerjack 是的——但是我来到这里寻找可以与 Meteor 一起使用的解决方案。我按照上面的答案并适应了 Meteor。也许其他人也会在这里登陆。
    • 在 4.0 这个答案重置了控件,这是 OP 一开始就试图避免的。
    • 取决于“重置控件”的含义。 OP 写道:“我需要在不完全重建 select2 的情况下进行这些更新,而只是刷新下拉列表中的项目。”这个解决方案应该做到这一点。请参阅参考的 GitHub 问题 - 我相信这是您在 4.0 中可以做的最好的事情。如果可以做得更好,请随时编辑。
    【解决方案4】:

    试试这个:

    var data = [{id: 1, text: 'First'}, {id: 2, text: 'Second'}, {...}];
    $('select[name="my_select"]').empty().select2({
        data: data
    });
    

    【讨论】:

    • 这段代码很容易理解:当我们清空选择框时 select2 会自动更新,当我们添加新选项的数据时 select2 会再次刷新。所以,不需要触发“change”和“change.select2”。
    • 这会重建 select2,这意味着如果您有任何配置,它们将丢失。如果您附加了任何事件,则需要先将它们分离,然后再重新附加。
    【解决方案5】:
    var selBoxObj = $('#selectpill');
    selBoxObj.trigger("change.select2");
    

    【讨论】:

      【解决方案6】:

      我通过使用 ajax 选项并指定自定义传输函数解决了这个问题。

      看到这个小提琴Select2 dynamic options demo

      这里是相关的 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++;
      });
      

      【讨论】:

        【解决方案7】:

        据我所知,如果不刷新整个列表或输入一些搜索文本并使用查询功能,就不可能更新 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 事件之前输入了搜索文本,仍然会有些奇怪。

        【讨论】:

        • 这些按钮将根据选择的内容进行 ajax 调用,它浮动在搜索输入上方的原因是因为我们无法将它们放在外面,没有空间放置它们。用户将使用搜索,我对其应用了 padding-left,因此文本在两个按钮之后开始......
        • 点击其中一个按钮之前下拉菜单的内容是什么?您可以让 ajax 调用关闭 select2 下拉菜单,然后再次打开它。您还可以让它抓取任何搜索文本并重新输入并触发按键,以使关闭/打开看起来不那么刺耳。
        • select2 不能在调用之间关闭和重新打开,只能创建一次,将调用填充下拉列表,之后,每次按下 Btn A 或 B 他们都会调用并仅刷新下拉内容,这是困难的部分:/
        【解决方案8】:

        对于 Select2 4.X

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

        【讨论】:

          【解决方案9】:

          answer given by SpinyMan 上的Diego's comment 很重要,因为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');
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-11-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-03-18
            • 1970-01-01
            相关资源
            最近更新 更多