无法在 Select2 下拉菜单中选择项目

Posted

技术标签:

【中文标题】无法在 Select2 下拉菜单中选择项目【英文标题】:Unable to select item in Select2 drop down 【发布时间】:2014-11-22 08:11:59 【问题描述】:

我正在开发一个使用 Select2(版本 3.5.1)的应用程序。设置此下拉/自动完成字段的 html 如下所示:

<input id="mySelect" class="form-control" type="hidden">

这个 sn-p 中的 form-control 类来自 Bootstrap。我正在使用以下内容从 javascript 初始化此字段:

function getItemFormat(item) 
  var format = '<div>' + item.ItemName + '</div>';
  return format;


$(function() 
  $('#mySelect').select2(
    minimumInputLength: 5,
    placeholder: 'Search for an item',
    allowClear: true,
    ajax: 
      url: '/api/getItems',
      dataType: 'json',
      quietMillis: 250,
      data: function (term, page) 
        return 
          query: term
        ;
      ,
      results: function (data, page) 
        return  results: data, id: 'ItemId', text: 'ItemText' ;
      
    ,
    formatResult: getItemFormat,
    dropdownCssClass: "bigdrop",
    escapeMarkup: function (m)  return m; 
  );
);

当我的选择字段加载时,它会成功呈现。一旦我至少输入了第五个字符,它就会成功地从服务器中提取项目并将它们列为选项。但是,如果我尝试选择其中之一,则不会发生任何事情。下拉弹出窗口保持打开状态。什么都没有放在实际的字段中。 JavaScript 控制台中没有错误。就像我没有点击任何东西一样。

此外,我注意到当我将鼠标放在某个项目上或尝试使用箭头键浏览选项列表时,没有任何突出显示。

我做错了什么?

【问题讨论】:

您似乎错过了 mySelect 部分的报价。是在你的来源吗?尝试添加它,看看是否有帮助 这是 sn-p 的问题(我已修复)。但是,这并没有解决问题。 @user70192 你能给我们提供一个使用 JSFiddle 的实例吗? 【参考方案1】:

发生了什么:

默认情况下,您在ajax.results 中返回的对象的results 应该是此结构[id:1,text:"a",id:2,text:"b", ...] 中的一个数组。

  results: function (data, page) 
    var array = data.results; //depends on your JSON
    return  results: array ;
  

在Select2.js 中它实际上声明:

* @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2.
*      The expected format is an object containing the following keys:
*      results array of objects that will be used as choices
*      more (optional) boolean indicating whether there are more results available
*      Example: results:[id:1, text:'Red',id:2, text:'Blue'], more:true

阅读源码,我们可以看到ajax.results在AJAX成功时被调用:

   success: function (data) 
                        // TODO - replace query.page with query so users have access to term, page, etc.
                        // added query as third paramter to keep backwards compatibility
                        var results = options.results(data, query.page, query);
                        query.callback(results);
                    

所以ajax.results实际上只是一个函数,用于在将数据传递给query.callback之前将数据格式化为适当的结构(例如[id:a,text:"a",id:b,text:"b", ...]):

 callback: this.bind(function (data) 

                    // ignore a response if the select2 has been closed before it was received
                    if (!self.opened()) return;


                    self.opts.populateResults.call(this, results, data.results, term: term, page: page, context:context);
                    self.postprocessResults(data, false, false);

                    if (data.more===true) 
                        more.detach().appendTo(results).html(self.opts.escapeMarkup(evaluate(self.opts.formatLoadMore, self.opts.element, page+1)));
                        window.setTimeout(function()  self.loadMoreIfNeeded(); , 10);
                     else 
                        more.remove();
                    
                    self.positionDropdown();
                    self.resultsPage = page;
                    self.context = data.context;
                    this.opts.element.trigger( type: "select2-loaded", items: data );
                ));

query.callback 最终所做的是正确设置逻辑,以便在您选择其中一项并触发 .selectChoice 时一切正常。

selectChoice: function (choice) 

            var selected = this.container.find(".select2-search-choice-focus");
            if (selected.length && choice && choice[0] == selected[0]) 

             else 
                if (selected.length) 
                    this.opts.element.trigger("choice-deselected", selected);
                
                selected.removeClass("select2-search-choice-focus");
                if (choice && choice.length) 
                    this.close();
                    choice.addClass("select2-search-choice-focus");
                    this.opts.element.trigger("choice-selected", choice);
                
            
         

因此,如果存在一些错误配置(例如,results 的结构不正确)导致在调用 .selectChoice 之前未将类 .select2-search-choice-focus 添加到 DOM 元素中,则会发生以下情况:

下拉弹出窗口保持打开状态。什么都没有放在实际的字段中。 JavaScript 控制台中没有错误。就像我没有点击任何东西一样。


解决方案

对此有很多解决方案。其中之一当然是在ajax.results 中进行一些数组键操作。

  results: function (data, page) 
  //data =  results:[ItemId:1,ItemText:"a",ItemId:2,ItemText:"b"] ;
    var array = data.results;
    var i = 0;
    while(i < array.length)
        array[i]["id"] = array[i]['ItemId'];
        array[i]["text"] = array[i]['ItemText'];
        delete array[i]["ItemId"];
        delete array[i]["ItemText"];
    i++;
    
    return  results: array ;
  

但你可能会问:为什么数组中的 id 必须是“id”,而 text 必须是“text”?

[id:1,text:"a",id:2,text:"b"] 

数组可以改为这种结构吗?

[ItemId:1,ItemText:"a",ItemId:2,ItemText:"b"]

答案是肯定的。您只需用自己的函数覆盖idtext 函数即可。

以下是Select2.js 中.selecte2 的原始函数:

    id: function (e)  return e == undefined ? null : e.id; ,
    text: function (e) 
      if (e && this.data && this.data.text) 
        if ($.isFunction(this.data.text)) 
          return this.data.text(e);
         else 
          return e[this.data.text];
        
       else 
        return e.text;
      
    ,

要覆盖它们,只需在要传递给.selecte2 的对象中添加您自己的函数:

$('#mySelect').select2(
id: function (item)  return item.ItemId ,
text: function (item)  return item.ItemText 
......
);

更新

还有什么情况:

但是,列表关闭后,所选项目的文本不会出现在字段中。

这意味着.selectChoice 已成功执行。现在问题出在.updateSelection。在源代码中:

   updateSelection: function (data) 

        var container=this.selection.find(".select2-chosen"), formatted, cssClass;

        this.selection.data("select2-data", data);

        container.empty();
        if (data !== null) 
            formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup);
        
        if (formatted !== undefined) 
            container.append(formatted);
        
        cssClass=this.opts.formatSelectionCssClass(data, container);
        if (cssClass !== undefined) 
            container.addClass(cssClass);
        

        this.selection.removeClass("select2-default");

        if (this.opts.allowClear && this.getPlaceholder() !== undefined) 
            this.container.addClass("select2-allowclear");
        
    

从这里我们可以看到,在将相应的文本字符串放入输入之前,它会调用formatSelection

formatSelection: function (data, container, escapeMarkup) 
            return data ? escapeMarkup(this.text(data)) : undefined;
        ,

更新:解决方案

以前我认为this.text(data) 可以通过在参数中包含text: funcion(item) ... 来覆盖,但遗憾的是它不是那样工作的。

因此,要在字段中正确呈现文本,您应该覆盖formatSelection

$('#mySelect').select2(
id: function (item)  return item.ItemId ,
formatSelection: function (item)  return item.ItemText 
//......
);

而不是尝试覆盖text(应该具有相同的效果,但库中尚不支持/实现这种覆盖方式)

$('#mySelect').select2(
id: function (item)  return item.ItemId ,
text: function (item)  return item.ItemText   //this will not work.
//......
);

【讨论】:

这种方法至少会关闭下拉列表。但是,列表关闭后,所选项目的文本不会出现在字段中。为什么不呢? @user70192 这是因为您必须提供formatSelection 功能,就像我在下面的回答中所说的那样。我在报告错误时遇到了同样的问题 @user70192 我已经更新了我的答案。简单地说,而不是 text: function (item) return item.ItemText ,我们只需要像这样更改 text into formatSelection`:formatSelection: function (item) return item.ItemText 就可以了。 很抱歉,但这个答案实际上扼杀了我使用 select2^^ 的愿望我的意思是答案可能是正确的,但是将工作放入一个简单的数组显示中绝对不值得疼痛;-) 是的,这是一个很长的答案,但 Archy 演示了如何遍历这个插件并弄清楚代码发生了什么(我知道我需要更经常地这样做)。我几乎没有读过它,但最终我很高兴我读了它,因为它确实帮助我找到了我的问题,但更了解为什么我的代码不起作用。一个++【参考方案2】:

您面临的问题是select2 希望您的所有结果都具有id 属性。如果不需要,您需要使用 id 函数进行初始化,该函数会从每个结果中返回 id。

除非您满足其中之一,否则它将不允许您选择结果。所以在你的例子中:

function getItemFormat(item) 
  var format = '<div>' + item.ItemName + '</div>';
  return format;


$(function() 
  $('#mySelect').select2(
    minimumInputLength: 5,
    placeholder: 'Search for an item',
    allowClear: true,
    id: function(item)  return item.ItemId; ,    /* <-- ADDED FUNCTION */
    ajax: 
      url: '/api/getItems',
      dataType: 'json',
      quietMillis: 250,
      data: function (term, page) 
        return 
          query: term
        ;
      ,
      results: function (data, page) 
        return  results: data, id: 'ItemId', text: 'ItemText' ;
      
    ,
    formatResult: getItemFormat,
    dropdownCssClass: "bigdrop",
    escapeMarkup: function (m)  return m; 
  );
);

【讨论】:

5 年后,但您的回答对我来说只是救命稻草。提前致谢!【参考方案3】:

您需要像@itsmejodie 所说的那样提供从您的 API 返回的 ID。 另一个问题是你必须提供select2formatResultformatSelection 函数,一旦你从Ajax 加载了它,但你不能把html 放在上面。例如:

function format (item)  
  return item.name; 


$(function() 
    $('#mySelect').select2(
      minimumInputLength: 2,
      placeholder: 'Search for an item',
      allowClear: true,
      ajax: 
        url: '/api/getItems',
        dataType: 'jsonp',
        quietMillis: 250,
        data: function (term, page) 
          return 
            query: term
          ;
        ,
        results: function (data, page) 
          return  results: data ;
        
      ,
      formatResult: format,
      formatSelection: format
    );
);

【讨论】:

【参考方案4】:

对于 Select2 的第 4 版使用

processResults: function (data)  

而不是

results: function (data) 

【讨论】:

以上是关于无法在 Select2 下拉菜单中选择项目的主要内容,如果未能解决你的问题,请参考以下文章

select2中的右对齐下拉菜单

如何设置 select2 下拉菜单的最小宽度和最大宽度?

select2 rails 下拉菜单无法正确呈现

ruby 从黄瓜的select2.js下拉菜单中选择一个选项

无法在 Select2 下拉列表中选择以相同开头的其他内容

在 yii2 的 select2 下拉菜单中设置默认值