Imansible selezionare l’elemento nel menu a discesa Select2

Sto lavorando su un’app che utilizza Select2 (versione 3.5.1). L’HTML per configurare questo campo a discesa / completamento automatico ha il seguente aspetto:

 

La class di form-control in questo frammento proviene da Bootstrap. Sto iniziando questo campo da JavaScript usando il seguente:

 function getItemFormat(item) { var format = '
' + item.ItemName + '
'; 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; } }); });

Quando viene caricato il campo selezionato, viene eseguito correttamente il rendering. Una volta digitato almeno il quinto carattere, esso estrae gli elementi dal server e li elenca come opzioni. Tuttavia, se provo a selezionarne uno, non succede nulla. Il popup a discesa rimane aperto. Nulla viene messo nel campo attuale. Non ci sono errori nella console JavaScript. È come se non avessi fatto clic su nulla.

Inoltre, ho notato che nulla viene evidenziato quando metto il mouse su un object o cerco di navigare nell’elenco delle opzioni con i tasti freccia.

Che cosa sto facendo di sbagliato?

Che cosa sta succedendo:

Per impostazione predefinita, i results dell’object che si sta restituendo in ajax.results devono essere un array in questa struttura [{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 in realtà afferma:

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

Leggendo il codice sorgente, possiamo vedere che ajax.results è chiamato al successo 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); } 

Quindi ajax.results è in realtà solo una funzione per la formattazione dei dati nella struttura appropriata (ad esempio [{id:a,text:"a"},{id:b,text:"b"}, ...] ) prima che i dati query.callback passati a 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 }); })}); 

E ciò che query.callback fine query.callback è impostare correttamente la logica in modo che tutto query.callback correttamente quando si sceglie uno degli elementi e si triggers .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); } } } 

Quindi, se c’è qualche errore di configurazione (ad es. I results non sono nella struttura corretta), la class .select2-search-choice-focus non deve essere aggiunta all’elemento DOM prima .selectChoice venga chiamato .selectChoice , questo è ciò che accade:

Il popup a discesa rimane aperto. Nulla viene messo nel campo attuale. Non ci sono errori nella console JavaScript. È come se non avessi fatto clic su nulla.


soluzioni

Ci sono molte soluzioni a questo. Uno di questi è, ovviamente, la manipolazione di alcuni tasti di array 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 }; } 

Ma potresti chiederti: perché l'id deve essere "id" e il testo deve essere "testo" nell'array?

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

L'array può essere in questa struttura?

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

La risposta è si. Hai solo bisogno di sovrascrivere le funzioni id e text con le tue funzioni.

Ecco le funzioni originali per .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; } }, 

Per sovrascriverli, aggiungi le tue funzioni all'interno dell'object che stai passando a .selecte2 :

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

aggiornamenti

Cos'altro sta accadendo:

Tuttavia, il testo dell'elemento selezionato non viene visualizzato nel campo dopo la chiusura dell'elenco.

Questo significa che .selectChoice è stato eseguito correttamente. Ora il problema si trova in .updateSelection . Nel codice sorgente:

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

Da qui possiamo vedere che, prima che la stringa di testo corrispondente sia inserita nell'input, chiamerebbe formatSelection .

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

Aggiornamento: soluzione

In precedenza ho pensato che this.text(data) può essere sovrascritto avendo text: funcion(item){ ... } nei parametri, ma purtroppo non funziona in questo modo.

Pertanto, per rendere correttamente il testo nel campo, devi sovrascrivere formatSelection

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

invece di cercare di sovrascrivere il text (che dovrebbe presumibilmente avere lo stesso effetto ma questo modo di sovrascrivere non è ancora supportato / implementato nella libreria)

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

Il problema che stai affrontando è che select2 vuole che tutti i tuoi risultati abbiano una proprietà id . Se non lo fanno, è necessario inizializzarsi con una funzione id che restituisce l’id da ogni risultato.

Non ti consentirà di selezionare un risultato a meno che tu non soddisfi uno di questi. Quindi nel caso del tuo esempio:

 function getItemFormat(item) { var format = '
' + item.ItemName + '
'; 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; } }); });

Devi fornire un ID che ritorni dalla tua API come ha detto @itsmejodie. L’altro problema è che devi fornire select2 formatResult e formatSelection , una volta caricato da Ajax ma non puoi inserire html su questo. per esempio:

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