Je travaille sur une application qui utilise Select2 (version 3.5.1). Le code HTML pour configurer ce champ déroulant/autocomplete ressemble à ceci:
<input id="mySelect" class="form-control" type="hidden">
La classe form-control
de cet extrait provient de Bootstrap. J'initialise ce champ à partir de JavaScript en utilisant les éléments suivants:
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; }
});
});
Lorsque mon champ de sélection est chargé, il s'affiche correctement. Une fois que je tape au moins le cinquième caractère, il extrait avec succès les éléments du serveur et les répertorie en tant qu'options. Cependant, si j'essaie de sélectionner l'un d'entre eux, rien ne se passe. La liste déroulante reste ouverte. Rien n'est mis dans le champ réel. Il n'y a pas d'erreur dans la console JavaScript. C'est comme si je n'avais rien cliqué.
De plus, j'ai remarqué que rien n'est mis en surbrillance lorsque je place la souris sur un élément ou que je tente de parcourir la liste des options avec les touches fléchées.
Qu'est-ce que je fais mal?
Par défaut, results
de l'objet que vous renvoyez dans ajax.results
doit être un tableau de cette structure [{id:1,text:"a"},{id:2,text:"b"}, ...]
.
results: function (data, page) {
var array = data.results; //depends on your JSON
return { results: array };
}
Dans Select2.js , il est en fait indiqué:
* @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}
En lisant le code source, nous pouvons voir que ajax.results
est appelé en cas de succès 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);
}
Donc, ajax.results
est en réalité une fonction permettant de formater vos données dans la structure appropriée (par exemple, [{id:a,text:"a"},{id:b,text:"b"}, ...]
) avant que les données ne soient transmises à 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 });
})});
Et ce que query.callback
finit par faire est de configurer correctement la logique afin que tout fonctionne correctement lorsque vous choisissez l'un des éléments et que vous déclenchez .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);
}
}
}
Ainsi, s'il existe une mauvaise configuration (par exemple, results
n'est pas dans la structure correcte) qui empêche la classe .select2-search-choice-focus
d'être ajoutée à l'élément DOM avant l'appel de .selectChoice
, voici ce qui se produit:
La liste déroulante reste ouverte. Rien n'est mis dans le champ réel. Il n'y a pas d'erreur dans la console JavaScript. C'est comme si je n'avais rien cliqué.
Il y a beaucoup de solutions à cela. L’un d’eux est, bien sûr, de manipuler des clés de tableaux dans 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 };
}
Mais vous pouvez vous demander: pourquoi l'identifiant doit-il être "id" et le texte, "text" dans le tableau?
[{id:1,text:"a"},{id:2,text:"b"}]
Le tableau peut-il être dans cette structure à la place?
[{ItemId:1,ItemText:"a"},{ItemId:2,ItemText:"b"}]
La réponse est oui. Vous devez simplement écraser les fonctions id
et text
avec vos propres fonctions.
Voici les fonctions d'origine de .selecte2
dans 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;
}
},
Pour les écraser, ajoutez simplement vos propres fonctions dans l'objet que vous transmettez à .selecte2
:
$('#mySelect').select2({
id: function (item) { return item.ItemId },
text: function (item) { return item.ItemText }
......
});
Toutefois, le texte de l'élément sélectionné n'apparaît pas dans le champ après la fermeture de la liste.
Cela signifie que .selectChoice
a été exécuté avec succès. Maintenant, le problème réside dans .updateSelection
. Dans le code source:
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");
}
}
À partir de là, nous pouvons voir que, avant que la chaîne de texte correspondante soit placée dans l'entrée, elle devrait s'appeler formatSelection
.
formatSelection: function (data, container, escapeMarkup) {
return data ? escapeMarkup(this.text(data)) : undefined;
},
Auparavant, je pensais que this.text(data)
pouvait être écrasé en ayant text: funcion(item){ ... }
dans les paramètres, mais malheureusement, cela ne fonctionne pas de cette façon.
Par conséquent, pour rendre le texte correctement dans le champ, vous devez écraser formatSelection
en faisant
$('#mySelect').select2({
id: function (item) { return item.ItemId },
formatSelection: function (item) { return item.ItemText }
//......
});
au lieu d'essayer d'écraser text
(ce qui devrait avoir le même effet mais cette méthode d'écrasement n'est pas encore prise en charge/implémentée dans la bibliothèque)
$('#mySelect').select2({
id: function (item) { return item.ItemId },
text: function (item) { return item.ItemText } //this will not work.
//......
});
Le problème que vous rencontrez est que select2
veut que tous vos résultats aient une propriété id
. Si ce n'est pas le cas, vous devez initialiser avec une fonction id
qui renvoie l'identifiant de chaque résultat.
Il ne vous permettra pas de sélectionner un résultat à moins que vous ne satisfaisiez l’un d’eux. Donc dans le cas de votre exemple:
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; }
});
});
Vous devez fournir un identifiant renvoyé par votre API, comme @itsmejodie, a déclaré… .. L'autre problème est que vous devez fournir les fonctions select2
formatResult
et formatSelection
, une fois que vous l'avez chargé depuis Ajax mais vous ne pouvez pas y ajouter html
. par exemple.:
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
});
});
Pour la version 4 de Select2, utilisez
processResults: function (data) {
au lieu de
results: function (data) {