it-swarm.com.de

Wählbare Optgroup mit select2

Ich habe select2 verwendet, um mehrere Optionen aus einem Dropdown-Menü auszuwählen. Was ich möchte ist, wenn der Benutzer die Optionsgruppe auswählt, sollten alle untergeordneten Optionen ausgewählt werden. Und ich möchte dies mit jQuery Select2 tun. Wie kann ich das machen?

10
Buvin Perera

Dies ist möglich, wenn Sie den Select2 mit einem ausgeblendeten Eingabeelement anstelle eines Auswahlelements sichern.

Um eine Gruppenoption auswählbar zu machen, müssen Sie ihr eine "ID" geben, aber es scheint, als könnte es sich um eine leere Zeichenfolge handeln. Sie können dann das Ereignis "select2-select" verwenden, um zu verhindern, dass die Gruppenoption ausgewählt wird, und stattdessen die untergeordneten Optionen ausgewählt werden.

Darüber hinaus kann eine query-Funktion bereitgestellt werden, um zu verhindern, dass eine Gruppenoption in der Liste angezeigt wird, nachdem alle untergeordneten Optionen ausgewählt wurden.

Wenn Sie Optionen wie folgt definiert haben:

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' }
        ]
    }
];

Sie können Ihr Select2 auf folgende Weise instrumentieren:

$('#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

Hier ist eine jsfiddle ohne die query-Funktion, bei der die Gruppenoptionen immer noch angezeigt werden, wenn alle ihre untergeordneten Optionen ausgewählt sind.

19
John S

Ich habe Johns Code verwendet, aber mein Problem war, dass ich die Fähigkeit zum Filtern brauchte, also habe ich ihn hinzugefügt. Sie können den Code auf jsfiddle sehen.

Dies ist der Abfragecode:

         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

Geben Sie zunächst eine ID für Ihre Auswahl Zum Beispiel

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

und fügen Sie dann Ihrer optgroup eine Klasse hinzu

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

und dann das hinzufügen

$('#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');
       }
    });

Wenn Sie auf optgroup klicken, werden alle Optionen unter der optgroup ausgewählt.

2
Maitri

In Select2 v4 habe ich festgestellt, dass die Antwort von John S nicht funktioniert ( siehe hier ). Ich lade meine Daten als Array mit AJAX und erstellte eine Problemumgehung: 

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

Ich gruppiere meine Artikel nach Speicherort, und jede Option enthält den Standortnamen irgendwo in ihrem HTML-Code. Jedes Mal, wenn auf einen Optgroup-Header geklickt wird, erhalte ich die Position (den Namen, der in der Dropdown-Liste angezeigt wird). Dann schaue ich alle Optionen in der Tabelle # select2 durch und finde heraus, welche die Position in ihrer HTML-Datei enthalten.

Ich weiß, dass dies eine harte Workaround ist, aber hoffentlich hilft/zeigt es in die richtige Richtung.

1
jgmackay

John S, bereitgestelltes Beispiel, funktioniert in den meisten Fällen sehr gut (für V3). Es hat jedoch einen Fehler:

Angenommen, die Auswahlliste ist lang genug, um einen Bildlauf durchzuführen. Wenn Sie ein Element in einer Gruppe auswählen, das nur nach dem Scrollen nach unten verfügbar ist, können Sie das nächste Element nach dem Element, das Sie aus dieser Gruppe ausgewählt haben, nicht mehr auswählen. Dies liegt daran, dass die selectHighlightVisible-Methode von select2 falsch funktioniert, da die von ihr verwendeten Selektoren die Annahme vertreten, dass die Gruppe immer "nicht auswählbar" ist. Die Bildlaufleiste springt also jedes Mal, wenn Sie versuchen, das Element auszuwählen.

Obwohl diese Lösung wirklich gut aussieht, habe ich sie leider fallen gelassen und ohne Gruppen-IDs neu implementiert:

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

und

  // 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

Nun, ich bin auf dieses Problem gestoßen und habe festgestellt, dass jedes Mal, wenn sich select2 (Select2 4.0.5) öffnet, ein span-Element vor dem schließenden body-Element hinzugefügt wird. Außerdem fügt das span-Element innerhalb des Bereichs ein ul mit der id: select2-X-results hinzu, wobei X die select2-ID ..__ ist. Daher fand ich die folgende Problemumgehung ( 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

Ich habe ein Plugin für Select2 v4 gefunden, das die Möglichkeit bietet, auf die Optgroup zu klicken, um alle untergeordneten Optionen auszuwählen bzw. die Auswahl aufzuheben. Es hat perfekt für mich funktioniert. bnjmnhndrsn/select2-optgroup-select

Danke, Ben Henderson!

0
Magno Alberto