web-dev-qa-db-ja.com

Select2ドロップダウンでアイテムを選択できません

Select2(バージョン3.5.1)を使用するアプリで作業しています。このドロップダウン/オートコンプリートフィールドを設定するHTMLは次のようになります。

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

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

選択フィールドがロードされると、正常にレンダリングされます。少なくとも5文字目を入力すると、サーバーからアイテムが正常にプルされ、オプションとしてリストされます。ただし、それらのいずれかを選択しようとしても、何も起こりません。ドロップダウンポップアップは開いたままです。実際のフィールドには何も入れられません。 JavaScriptコンソールにエラーはありません。何もクリックしなかったようです。

さらに、アイテムの上にマウスを置いたり、矢印キーでオプションのリストをナビゲートしようとしても、何も強調表示されないことに気付きました。

何が間違っていますか?

28
user70192

何が起こっている:

デフォルトでは、_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:で呼び出されていることがわかります。

_   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_は、データが_[{id:a,text:"a"},{id:b,text:"b"}, ...]_に渡される前に、データを適切な構造(たとえば_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 });
                })});
_


そして_query.callback_が最終的に行うことは、アイテムの1つを選択して_.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);
                }
            }
        } 
_


したがって、_.select2-search-choice-focus_が呼び出される前にクラス_.selectChoice_がDOM要素に追加されない原因となる何らかの構成ミス(たとえば、resultsが正しい構造にない)がある場合、これが起こります:

ドロップダウンポップアップは開いたままです。実際のフィールドには何も入れられません。 JavaScriptコンソールにエラーはありません。何もクリックしなかったようです。


解決策

これには多くの解決策があります。それらの1つは、もちろん、_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"でなければならないのですか?

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

代わりに、配列をこの構造にすることはできますか?

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

答えはイエスです。 idおよびtext関数を独自の関数で上書きするだけです。


これは、 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.
//......
});
_
47
Archy Will He

あなたが直面している問題は、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; }
  });
});
8
itsmejodie

@itsmejodieが言ったように、APIから返されるIDを提供する必要があります。もう1つの問題は、select2formatResultおよびformatSelection関数を提供する必要があることです。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
    });
});
2