本文介绍了无法在Select2下拉菜单中选择项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Select2(版本3.5.1)的应用程序。设置此下拉列表/自动填充字段的HTML如下所示:

 < input id =mySelectclass =form -controltype =hidden> 

此片段中的表格控制类来自Bootstrap。我正在使用下面的JavaScript初始化这个字段:

  function getItemFormat(item){
var format ='< ; div>'+ item.ItemName +'< / div>';
返回格式;


$(function(){
$('#mySelect')。select2({
minimumInputLength:5,
placeholder:'Search '
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控制台中没有错误。它就像我没有点击任何东西。



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



我在做什么错了?

发生了什么:

默认情况下, ajax.results 中返回的对象的结果 code>应该是这个结构中的数组 [{id:1,text:a},{id:2,text:b},...]

  results:function(data,page){
var array = data.results; //取决于你的JSON
return {results:array};
}



在中,它实际上指出:

  * @param options.results一个函数(remoteData,pageNumber,query),用于将从远程请求返回的数据转换为Select2预期的格式。 
*期望的格式是包含以下键的对象:
*结果将用作选项的对象数组
* more(可选)布尔值,指示是否有更多结果可用
*示例:{results:[{id:1,text:'Red'},{id:2,text:'Blue'}],more:true}




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

  success:function(data){
// TODO - 用查询替换query.page,以便用户可以访问术语,页面等。
//将查询添加为第三参数以保持向后兼容性
var results = options.results(data, query.page,query);
query.callback(results);
}



<所以 ajax.results 实际上只是一个函数,可以将数据格式化为合适的结构(例如 [{id:a,text:在将数据传递给 query.callback >:

  callback:this.bind(function(data){

//忽略响应如果select2在收到之前已经关闭
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.set Timeout(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});
})});




code> query.callback 最终会做的是正确设置逻辑,以便在选择其中一个项目时触发 .selectChoice $ b

  selectChoice:function(choice){

var selected = this.container。找到(选择2 - 搜索 - 选择焦点。);
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);
}
}
}




因此,如果某些配置错误(例如结果不在正确的结构中)导致类 .select2-search-choice-focus 在调用 .selectChoice 之前不会被添加到DOM元素中,这就是发生的情况: p>






解决方案



有很多解决方案。当然,其中之一是在 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'];
删除数组[i] [ItemId];
删除数组[i] [ItemText];
i ++;
}
return {results:array};
}

但是你可能会问:为什么id必须是id在数组中是文本?

  [{id:1,text:a},{id:2,text:b}] 

数组可以在这个结构中吗?

  [{ItemId:1,ItemText:a},{ItemId:2,ItemText:b}] 

答案是肯定的。您只需用自己的函数覆盖 id 和 text 函数。





以下是:

  id:function(e){返回e ==未定义? null:e.id; (e& amp; this.data&& this.data.text){
if($ .isFunction(this。),
text:function(e){
if 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-selected),格式化,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} //这不起作用
// ......
});


I am working on an app that uses Select2 (version 3.5.1). The HTML to setup this drop down / autocomplete field looks like this:

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

The form-control class in this snippet comes from Bootstrap. I am initializing this field from JavaScript using the following:

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

When my select field loads, it successfully renders. Once I type at least the fifth character, it successfully pulls items from the server and lists them as options. However, if I try to select one of them, nothing happens. The drop-down popup stays open. Nothing gets put in the actual field. There are no errors in the JavaScript console. Its like I didn't click anything.

In addition, I noticed that nothing is highlighted when I put my mouse over an item or attempt to navigate the list of options with the arrow keys.

What am I doing wrong?

解决方案

What is happening:

By default, results of the object you are returning in ajax.results should be an array in this structure [{id:1,text:"a"},{id:2,text:"b"}, ...].

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


In Select2.js it actually states:

* @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}


Reading the source code, we can see that ajax.results is called on AJAX success:

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


So ajax.results is really just a function for you to format your data into the appropriate structure ( e.g. [{id:a,text:"a"},{id:b,text:"b"}, ...]) before the data is passed to query.callback:

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


And what query.callback eventually does is to set the logic up properly so that everything works fine when you choose one of the items and trigger .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);
                }
            }
        }


So if there is some misconfiguration (e.g. results is not in the correct structure) that causes the class .select2-search-choice-focus not to be added to the DOM element before .selectChoice is called, this is what happens:


Solutions

There are many solutions to this. One of them is, of course, do some array keys manipulation in 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 };
  }

But you may ask: why must the id be "id" and the text be "text" in the array?

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

Can the array be in this structure instead?

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

The answer is yes. You just need to overwrite the id and text functions with your own functions.


Here are the original functions for .selecte2 in Select2.js:

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


To overwrite them, just add your own functions inside the object you are passing to .selecte2:

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


Updates

What else is happening :

This means .selectChoice has been successfully executed. Now the problem lies in .updateSelection. In the source code:

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


From here we can see that, before the corresponding string of text is placed into the input, it would call formatSelection.

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

Update: Solution

Previously I thought this.text(data) can be overwritten by having text: funcion(item){ ... } in the parameters, but sadly it doesn't work that way.

Therefore to render the text properly in the field, you should overwrite formatSelection by doing

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

instead of trying to overwrite text (which should supposedly have the same effect but this way of overwriting is not yet supported/implemented in the library)

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

这篇关于无法在Select2下拉菜单中选择项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-13 14:20