django-autocomplete-light icon indicating copy to clipboard operation
django-autocomplete-light copied to clipboard

Option to select all matching entries in multi selects

Open marekjedrzejewski opened this issue 7 years ago • 6 comments

Hi again :) I would like to be able to select all matching entries, tried using code from https://github.com/select2/select2/issues/195#issuecomment-240130634 (jsbin showing it), but when I use it, no options previously inside form are listed. Any simple way to make it work, or that's more complicated issue?

Edit: Thrown the code inside the form template, if that's relevant.

marekjedrzejewski avatar Apr 25 '17 17:04 marekjedrzejewski

It works for me when I select "California", then open select2 again and click "Select All". It moved a bit, but it's there. How to reproduce your issue exactly ?

jpic avatar Apr 25 '17 21:04 jpic

I tried to use it on modelform which has 2 autocomplete fields. Person, that is directly taken from model and Report, that queries reports based on person chosen. JS is simply dumped to this form template js section. When I'll be back home I'll try to reproduce it on test project.

marekjedrzejewski avatar Apr 26 '17 09:04 marekjedrzejewski

The same behavior can be observed in test project - added script to select2_gm2m template: https://github.com/marekjedrzejewski/django-autocomplete-light/commit/3cb7006577221d75dd8fb8ac24a09d69edb04e0b and 'select all' button appears within form fields, but options are no longer there.

marekjedrzejewski avatar Apr 27 '17 10:04 marekjedrzejewski

@jpic Did you have any chance to look at this?

marekjedrzejewski avatar May 07 '17 11:05 marekjedrzejewski

Any insight on this? :)

marcodelmoral avatar May 06 '18 10:05 marcodelmoral

I needed this implemented with ajax(because of the large select options). Here is my overly hacky/completed solution for this:

In form field i keep the default widget and don't use dal widget. Also I set the queryset to none since I will be using the ajax to get the items.

forms.py


    terms = ModelMultipleChoiceField(
        queryset=Term.objects.none(),
        required=False,
        # widget=autocomplete.ModelSelect2(url='dove:term_autocomplete')
    )

Difference in view class is that I set the paginate_by to a high number so I don't have to deal with pagination.

views.py


class TermAutocomplete(autocomplete.Select2QuerySetView):
    paginate_by = 1000

    def get_queryset(self):
        # Don't forget to filter out results depending on the visitor !
        if not self.request.user.is_authenticated:
            return Term.objects.none()

        qs = Term.objects.all()

        if self.q:
            qs = qs.filter(label__icontains=self.q)
        return qs

In javascript:

  • I manually initiate the select2 on the select field.
  • We get objects with ajax call. However objects when displayed in select2 dropdown don't have their pk.
  • Using the select2 templateResult I added back object pk to dropdown li elements.
  • After search is made I added at the top of the search field a "Select All" button.
  • Since there are no options in select we add the options using the pk's we added with template.
  • select them all using .val()

function formatResult(r) {
    return $('<span id="select2-result-id-' + r.id + '">' + r.text + '</span>');
}

  $("#id_terms").select2({
      minimumInputLength: 3,
      templateResult: formatResult,
      ajax: {
        url: "{% url 'dove:term_autocomplete' %}",
	dataType: 'json',
        cache: false,
	// Additional AJAX parameters go here; see the end of this chapter for the full code of this example
      }
  });

  var terms_search_field = $("#id_terms").data("select2").$selection.find('.select2-search__field');
  var terms_label =  $("#geneset-form-div")[0].querySelector("label[for='id_terms']")
  terms_label.innerHTML += '<button type="button" id="clear-all-terms">Clear All</button>'

  $("#clear-all-terms")[0].addEventListener("click", function(){
    $("#id_terms").val(null).trigger("change");
  });

  terms_search_field[0].addEventListener("keyup", function(){
      if (terms_search_field.val().length >= 3) {
        var results = $("#select2-id_terms-results")[0];
        if (!results.innerHTML.includes("select-all-matched-terms")) {
          results.innerHTML = '<li><button type="button" id="select-all-matched-terms">Select All</button></li>' + results.innerHTML
        }

        $("#select-all-matched-terms")[0].addEventListener("click", function(){
          var matches = [];
          $.each($(".select2-results__options").find('li'), function(i, item) {
              var term_id = item.children[0].id.split("-").splice(-1)[0];
              var term_text = item.children[0].textContent;
              if (term_id.length > 0 && term_id != "terms"){
                if ($('#id_terms').find("option[value='" + term_id + "']").length) {
                    $('#id_terms').val(term_id).trigger('change');
                } else { 
                    // Create a DOM Option and pre-select by default
                    var newOption = new Option(term_text, term_id, true, true);
                    // Append it to the select
                    $('#id_terms').append(newOption).trigger('change');
                } 
                matches.push(term_id);
              }
          });
          $("#id_terms").val(matches).trigger("change");
          terms_search_field[0].value = "";
          $("#id_terms").select2("close");
        });
      }
  // Do something
  });

Lastly since we set the queryset to none at the begining django won't accept the values. To solve this we set the queryset to all in the POST forms init function.

form.py


    def __init__(self, post=False, *args, **kwargs):
        super(GeneSetForm, self).__init__(*args, **kwargs)
        if post:
            self.fields['terms'].queryset = Term.objects.all()


Initiate the form class with post True.

views.py

` form = GeneSetForm(True, request.POST)``

barslmn avatar Dec 02 '20 20:12 barslmn