web-dev-qa-db-fra.com

Optgroup sélectionnable en utilisant select2

J'ai utilisé select2 pour sélectionner plusieurs options dans une liste déroulante, mais est-il possible pour select2 de sélectionner le groupe de contrôle complet ?? Ce que je veux, c'est lorsque l'utilisateur sélectionne le groupe d'options, toutes les options enfants doivent être sélectionnées. Et je veux faire cela en utilisant jQuery Select2. Comment pourrais-je faire ça?

10
Buvin Perera

Cela est possible si vous sauvegardez la Select2 avec un élément d’entrée masqué - au lieu d’un élément de sélection.

Pour rendre une option de groupe sélectionnable, vous devez lui attribuer un "id", mais il semble que cela puisse être une chaîne vide. Vous pouvez ensuite utiliser l'événement "select2-select" pour empêcher la sélection de l'option de groupe et faire en sorte que ses options enfants soient sélectionnées.

De plus, une fonction query peut être fournie pour empêcher une option de groupe d'apparaître dans la liste après la sélection de toutes ses options enfants.

Si vous avez des options définies comme ceci:

var FRUIT_GROUPS = [
    {
        id: '',
        text: 'Citrus',
        children: [
            { id: 'c1', text: 'Grapefruit' },
            { id: 'c2', text: 'Orange' },
            { id: 'c3', text: 'Lemon' },
            { id: 'c4', text: 'Lime' }
        ]
    },
    {
        id: '',
        text: 'Other',
        children: [
            { id: 'o1', text: 'Apple' },
            { id: 'o2', text: 'Mango' },
            { id: 'o3', text: 'Banana' }
        ]
    }
];

Vous pouvez instrumenter votre Select2 comme ceci:

$('#fruitSelect').select2({
    multiple: true,
    placeholder: "Select fruits...",
    data: FRUIT_GROUPS,
    query: function(options) {
        var selectedIds = options.element.select2('val');
        var selectableGroups = $.map(this.data, function(group) {
            var areChildrenAllSelected = true;
            $.each(group.children, function(i, child) {
                if (selectedIds.indexOf(child.id) < 0) {
                    areChildrenAllSelected = false;
                    return false; // Short-circuit $.each()
                }
            });
            return !areChildrenAllSelected ? group : null;
        });
        options.callback({ results: selectableGroups });
    }
}).on('select2-selecting', function(e) {
    var $select = $(this);
    if (e.val == '') { // Assume only groups have an empty id
        e.preventDefault();
        $select.select2('data', $select.select2('data').concat(e.choice.children));
        $select.select2('close');
    }
});

jsfiddle

Voici un jsfiddle sans la fonction query, où les options de groupe apparaissent toujours lorsque toutes leurs options enfants sont sélectionnées.

19
John S

J'ai utilisé le code de John, mais mon problème était que j'avais besoin de la possibilité de filtrer, alors je l'ai ajouté. Vous pouvez voir que le code fonctionne sur jsfiddle .

C'est le code de la requête:

         query: function (options) {
            var selectedIds = options.element.select2('val');
            var data = jQuery.extend(true, {}, FRUIT_GROUPS);
            var selectableGroups = $.map(data, function (group) {
                var areAllChildrenSelected = true,
                    parentMatchTerm = false,
                    anyChildMatchTerm = false;
                if (group.text.toLowerCase().indexOf(options.term.toLowerCase()) >= 0) {
                    parentMatchTerm = true;
                }
                var i = group.children.length
                while (i--) {
                    var child = group.children[i];

                    if (selectedIds.indexOf(child.id) < 0) {
                        areAllChildrenSelected = false;
                    };

                    if (options.term == '' || (child.text.toLowerCase().indexOf(options.term.toLowerCase()) >= 0)) {
                        anyChildMatchTerm = true;
                    }
                    else if (!parentMatchTerm) {
                        var index = group.children.indexOf(child);
                        if (index > -1) {
                            group.children.splice(index, 1);
                        };
                    };
                };

                return (!areAllChildrenSelected && (parentMatchTerm || anyChildMatchTerm)) ? group : null;
            });

            options.callback({ results: selectableGroups });
        }
2
mgalindez

Donnez d’abord l’identifiant à votre choix Par exemple

<select style="width: 95%" id="selectgroup">

puis ajoutez la classe à votre optgroup comme

 <optgroup value="ATZ" label="Alaskan/Hawaiian Time Zone" class="select2-result-selectable">

puis ajoutez ceci

$('#selectgroup').select2({

    }).on('select2-selecting', function (e) {
        debugger;
        var $select = $(this);
        if (e.val == undefined) {
            e.preventDefault();
            var childIds = $.map(e.choice.children, function (child) {
                return child.id;
            });
            $select.select2('val', $select.select2('val').concat(childIds));
            $select.select2('close');
       }
    });

Si vous cliquez sur optgroup, toutes les options figurant sous optgroup seront sélectionnées.

2
Maitri

Dans Select2 v4, j’ai constaté que la réponse de John S ne fonctionnait pas ( voir ici ). Je charge mes données sous forme de tableau à l'aide de AJAX et j'ai créé une solution de contournement: 

$(document).on("click", ".select2-results__group", function(){
    var input = $(this);
    var location = input.html();
    // Find the items with this location
    var options = $('#select2 option');
    $.each(options, function(key, value){
        var name = $(value).html();
        // The option contains the location, so mark it as selected
        if(name.indexOf(location) >= 0){
            $(value).prop("selected","selected");
        }
    });
    $("#select2").trigger("change");
});

Je regroupe mes éléments par emplacement et chaque option contient le nom de l'emplacement quelque part dans son code HTML. Chaque fois qu'un en-tête optgroup est cliqué, j'obtiens son emplacement (le nom affiché dans la liste déroulante). Ensuite, je regarde toutes les options de la table # select2 et trouve celles qui contiennent cet emplacement dans son html.

Je sais que ceci est une solution de contournement, mais j'espère que cela aidera/va dans la bonne direction.

1
jgmackay

John S, l'exemple fourni fonctionne vraiment bien (pour V3) dans la plupart des cas. Cependant, il y a un bug:

En supposant que la liste de sélection est assez longue pour avoir défiler. Lorsque vous sélectionnez un élément d'un groupe disponible uniquement après avoir fait défiler l'écran vers le bas, vous ne pouvez plus sélectionner l'élément suivant après celui que vous avez sélectionné dans ce groupe. Cela est dû au fait que la méthode sureHighlightVisible de select2 commence à se comporter de manière incorrecte, car les sélecteurs qu’elle utilise sont basés sur des hypothèses, ce groupe étant toujours 'non sélectionnable'. Ainsi, le parchemin sautera chaque fois que vous essayez de sélectionner l'élément.

Donc, malheureusement, bien que cette solution semble vraiment bonne - je l'ai abandonnée et ré-implémentée sans utiliser d'identifiant de groupe:

$selectEl..on("select2-open", function(event) {
          $(event.target).data("select2").dropdown.on("click", "li.select2-result-unselectable", selectGroup);
          $(event.target).data("select2").dropdown.on("mousemove-filtered", "li.select2-result-unselectable", highlight);
        }).on("select2-close", function(event) {
          $(event.target).data("select2").dropdown.off("click", "li.select2-result-unselectable", selectGroup);
          $(event.target).data("select2").dropdown.off("mousemove-filtered", "li.select2-result-unselectable", highlight);
        });

et

  // selection of the group.
  function selectGroup(e) {
    var $li = $(this);
    e.preventDefault();
    $select.select2('data', $select.select2('data').concat($li.data("select2Data").children));
    $select.select2('close');
    _this.$field.trigger('change');
  }

  // highlight of the group.
  function highlight(e) {
    if ($(e.target).hasClass("select2-result-unselectable") || $(e.target.parentNode).hasClass('select2-result-unselectable')) {
      e.preventDefault();
      e.stopPropagation();
      $select.data("select2").dropdown.find(".select2-highlighted").removeClass("select2-highlighted");
      $(this).addClass("select2-highlighted");
    }
  }
0
Ilya Shaikovsky

J'ai rencontré ce problème et je constate que chaque fois que select2 (Select2 4.0.5) s'ouvre, il ajoute un élément span avant l'élément body de fermeture. De plus, l'intérieur de l'élément span ajoute un ul avec l'id: select2-X-results, où X est l'identifiant select2 . J'ai donc trouvé la solution suivante ( jsfiddle ):

var countries = [{
  "id": 1,
  "text": "Greece",
  "children": [{
    "id": "Athens",
    "text": "Athens"
  }, {
    "id": "Thessalonica",
    "text": "Thessalonica"
  }]
}, {
  "id": 2,
  "text": "Italy",
  "children": [{
    "id": "Milan",
    "text": "Milan"
  }, {
    "id": "Rome",
    "text": "Rome"
  }]
}];

$('#selectcountry').select2({
  placeholder: "Please select cities",
  allowClear: true,
  width: '100%',
  data: countries
});

$('#selectcountry').on('select2:open', function(e) {

  $('#select2-selectcountry-results').on('click', function(event) {

    event.stopPropagation();
    var data = $(event.target).html();
    var selectedOptionGroup = data.toString().trim();

    var groupchildren = [];

    for (var i = 0; i < countries.length; i++) {


      if (selectedOptionGroup.toString() === countries[i].text.toString()) {

        for (var j = 0; j < countries[i].children.length; j++) {

          groupchildren.Push(countries[i].children[j].id);

        }

      }


    }


    var options = [];

    options = $('#selectcountry').val();

    if (options === null || options === '') {

      options = [];

    }

    for (var i = 0; i < groupchildren.length; i++) {

      var count = 0;

      for (var j = 0; j < options.length; j++) {

        if (options[j].toString() === groupchildren[i].toString()) {

          count++;
          break;

        }

      }

      if (count === 0) {
        options.Push(groupchildren[i].toString());
      }
    }

    $('#selectcountry').val(options);
    $('#selectcountry').trigger('change'); // Notify any JS components that the value changed
    $('#selectcountry').select2('close');    

  });
});
li.select2-results__option strong.select2-results__group:hover {
  background-color: #ddd;
  cursor: pointer;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.full.min.js"></script>


<h1>Selectable optgroup using select2</h1>
<select id="selectcountry" name="country[]" class="form-control" multiple style="width: 100%"></select>

0
Sofoklis

J'ai trouvé un plugin pour Select2 v4 qui ajoute la possibilité de cliquer sur l'optgroup pour sélectionner/désélectionner toutes les options enfants. Cela a fonctionné parfaitement pour moi. bnjmnhndrsn/select2-optgroup-select

Merci Ben Henderson!

0
Magno Alberto