web-dev-qa-db-fra.com

Mettre à jour les données select2 sans reconstruire le contrôle

Je suis en train de convertir un <input type="hidden"> en une liste déroulante select2 et de lui transmettre des données via la méthode d'interrogation

$('#inputhidden').select2({
    query: function( query ) {
        query.callback( data ); // the data is in the format select2 expects and all works well..
    );
});

Le problème est que je devais pirater l'interface utilisateur de select2 et positionner deux boutons en haut de la barre de recherche qui, lorsque l'utilisateur clique dessus, effectuera des appels ajax et devra mettre à jour le contenu de select2.

enter image description here

Maintenant, j'ai besoin que ces mises à jour se produisent sans reconstruire entièrement le select2, mais plutôt en rafraîchissant les éléments du menu déroulant. Je n'arrive pas à trouver un moyen de transmettre un nouvel ensemble de données à un contrôle select2 déjà créé, est-ce possible?

35
mfreitas

select2 v3.x

Si vous avez un tableau local avec des options (reçu par un appel ajax), je pense que vous devriez utiliser le paramètre data comme fonction renvoyant les résultats pour la zone de sélection:

var pills = [{id:0, text: "red"}, {id:1, text: "blue"}]; 

$('#selectpill').select2({
    placeholder: "Select a pill",
    data: function() { return {results: pills}; }
});

$('#uppercase').click(function() {
    $.each(pills, function(idx, val) {
        pills[idx].text = val.text.toUpperCase();
    });
});
$('#newresults').click(function() {
    pills = [{id:0, text: "white"}, {id:1, text: "black"}];
});

VIOLON: http://jsfiddle.net/RVnfn/576/

Si vous personnalisez l'interface select2 avec des boutons, appelez simplement updateResults (cette méthode n'est pas autorisée à appeler depuis un objet select de out2 mais vous pouvez l'ajouter à un tableau allowedMethods dans select2 si nécessaire), méthode après la mise à jour d'un tableau de données (pills par exemple) .


select2 v4: adaptateur de données personnalisé

Un adaptateur de données personnalisé avec la méthode updateOptions supplémentaire (la raison pour laquelle la méthode ArrayAdapter originale n’a pas cette fonctionnalité) peut être utilisé pour mettre à jour de manière dynamique la liste d’options (toutes les options de cet exemple):

$.fn.select2.AMD.define('select2/data/customAdapter',
    ['select2/data/array', 'select2/utils'],
    function (ArrayAdapter, Utils) {
        function CustomDataAdapter ($element, options) {
            CustomDataAdapter.__super__.constructor.call(this, $element, options);
        }
        Utils.Extend(CustomDataAdapter, ArrayAdapter);
        CustomDataAdapter.prototype.updateOptions = function (data) {
            this.$element.find('option').remove(); // remove all options
            this.addOptions(this.convertToOptions(data));
        }        
        return CustomDataAdapter;
    }
);

var customAdapter = $.fn.select2.AMD.require('select2/data/customAdapter');

var sel = $('select').select2({
    dataAdapter: customAdapter,
    data: pills
});

$('#uppercase').click(function() {
    $.each(pills, function(idx, val) {
        pills[idx].text = val.text.toUpperCase();
    });
    sel.data('select2').dataAdapter.updateOptions(pills);
});

VIOLON: https://jsfiddle.net/xu48n36c/1/


select2 v4: fonction de transport ajax

dans la v4, vous pouvez définir une méthode de transport personnalisée pouvant fonctionner avec un tableau de données local (thx @Caleb_Kiage, par exemple, j'ai joué sans succès)

docs: https://select2.github.io/options.html#can-an-ajax-plugin-other-than-jqueryajax-be-used

Select2 utilise la méthode de transport définie dans ajax.transport pour envoyer demandes à votre API. Par défaut, cette méthode de transport est jQuery.ajax mais cela peut être changé.

$('select').select2({
    ajax: {
        transport: function(params, success, failure) {
            var items = pills;
            // fitering if params.data.q available
            if (params.data && params.data.q) {
                items = items.filter(function(item) {
                    return new RegExp(params.data.q).test(item.text);
                });
            }
            var promise = new Promise(function(resolve, reject) {
                resolve({results: items});
            });
            promise.then(success);
            promise.catch(failure);
        }
    }
});

MAIS avec cette méthode, vous devez modifier les identifiants des options si le texte de l'option dans le tableau est modifié - la liste des éléments d'option opt internes de select2 dom n'a pas été modifiée. Si l'id de l'option dans le tableau reste identique, l'option enregistrée précédemment sera affichée au lieu d'être mise à jour à partir du tableau! Ce n'est pas un problème si array n'est modifié que par l'ajout de nouveaux éléments - ok pour la plupart des cas courants.

FIDDLE: _ ​​https://jsfiddle.net/xu48n36c/3/

43
ndpu

Je pense qu'il suffit de transmettre les données directement: 

$("#inputhidden").select2("data", data, true);

Notez que le deuxième paramètre semble indiquer qu'une actualisation est souhaitée.

Merci à @Bergi pour son aide avec this


Si cela ne se met pas automatiquement à jour, vous pouvez aussi appeler directement sa méthode updateResults. 

$("#inputhidden").select2("updateResults");

Ou le déclencher indirectement en envoyant un déclencheur à "input.select2-input" comme ceci:

var search = $("#inputhidden input.select2-input");
search.trigger("input");
17
zsawyer

En utilisant Select2 4.0 avec Meteor, vous pouvez faire quelque chose comme ceci:

Template.TemplateName.rendered = ->

  $("#select2-el").select2({
    data  : Session.get("select2Data")
  })

  @autorun ->

    # Clear the existing list options. 
    $("#select2-el").select2().empty()

    # Re-create with new options.
    $("#select2-el").select2({
      data  : Session.get("select2Data")
    })

Que ce passe-t-il: 

  1. Quand le template est rendu ...
  2. Initiez un contrôle select2 avec les données de la session.
  3. La fonction @autorun (this.autorun) s'exécute chaque fois que la valeur de Session.get ("select2Data") change.
  4. Chaque fois que la session change, effacez les options select2 existantes et recréez de nouvelles données.

Cela fonctionne pour toutes les sources de données réactives, telles que Collection.find (). Fetch (), et pas uniquement pour Session.get (). 

REMARQUE: à partir de Select2 version 4.0, vous devez supprimer les options existantes avant d’ajouter de nouvelles onces. Voir ce problème GitHub pour plus de détails . Il n'y a pas de méthode pour "mettre à jour les options" sans effacer les options existantes. 

Ce qui précède est coffeescript. Très similaire pour Javascript. 

12
Data Meister

J'ai résolu ce problème en utilisant l'option ajax et en spécifiant une fonction de transport personnalisée.

voir ce violon Select2 démo d'options dynamiques

Voici le js pertinent pour que cela fonctionne.

var $items = [];

let options = {
ajax: {
    transport: function(params, success, failure) {
    let items = $items;

    if (params.data && params.data.q) {
        items = items.filter(function(item) {
        return new RegExp(params.data.q).test(item.text);
        });
    }

    let promise = new Promise(function(resolve, reject) {
        resolve({
        results: items
        });
    });

    promise.then(success);
    promise.catch(failure);
    }
},
placeholder: 'Select item'
};

$('select').select2(options);

let count = $items.length + 1;

$('button').on('click', function() {
$items.Push({
    id: count,
    text: 'Item' + count
});
count++;
});
3
Caleb Kiage

var selBoxObj = $ ('# selectpill'); selBoxObj.trigger ("change.select2");

2
karthipan raj

Pour Select2 4.X

var instance = $('#select2Container').data('select2');
var searchVal = instance.dropdown.$search.val();
instance.trigger('query', {term:searchVal});
1
Andrey Ganzevich

Autant que je sache, il n’est pas possible de mettre à jour les options de select2 sans actualiser la liste complète ou entrer du texte de recherche et en utilisant une fonction de requête.

Que sont censés faire ces boutons? S'ils sont utilisés pour déterminer les options de sélection, pourquoi ne pas les mettre en dehors de la zone de sélection et les laisser définir par programme les données de la boîte de sélection, puis les ouvrir? Je ne comprends pas pourquoi vous voudriez les mettre en haut du champ de recherche. Si l'utilisateur n'est pas supposé effectuer une recherche, vous pouvez utiliser l'option minimumResultsForSearch pour masquer la fonctionnalité de recherche.

Edit: Qu'en est-il de cela ...

HTML:

<input type="hidden" id="select2" class="select" />

Javascript

var data = [{id: 0, text: "Zero"}],
    select = $('#select2');

select.select2({
  query: function(query) {
    query.callback({results: data});
  },
  width: '150px'
});

console.log('Opening select2...');
select.select2('open');

setTimeout(function() {
  console.log('Updating data...');
  data = [{id: 1, text: 'One'}];
}, 1500);

setTimeout(function() {
  console.log('Fake keyup-change...');
  select.data().select2.search.trigger('keyup-change');
}, 3000);

Exemple: Plunker

Edit 2: Cela permettra au moins de mettre la liste à jour, mais il y a toujours quelque chose de bizarre si vous avez saisi un texte de recherche avant de déclencher l'événement keyup-change.

1
rtcherry

Essaye celui-là: 

var data = [{id: 1, text: 'First'}, {id: 2, text: 'Second'}, {...}];
$('select[name="my_select"]').empty().select2({
    data: data
});
0
SpinyMan