Denis Drescher Denis Drescher - 2 months ago 28
Ajax Question

Select2 removes all put the first tag

I’m using Select2 for a many-to-many relation in Django. Due to all sorts of validation constraints, I’ve found it easiest to create the related objects via an AJAX request right when they’re entered into the Select2 tag field. Below a minimal example (and a Fiddle).

The HTML:

<select class="js-example-tags form-control" multiple="" tabindex="-1" aria-hidden="true"></select>


A bit of CSS:

.js-example-tags {
width: 100%;
}


And the JS:

function register(event) {
console.log(event);
if (event.params.data.id == event.params.data.text) {
$.ajax({
type: 'POST',
url: 'https://www.random.org/integers/?num=1&min=0&max=999&col=1&base=10&format=plain&rnd=new',
success: function(data) {
$('option[value="' + event.params.data.text + '"').attr('value', data);
},
error: function(jqXHR, textStatus, errorThrown) {
$('option[value="' + event.params.data.id + '"]').remove();
}
});
}
}


$(".js-example-tags").select2({
tags: true,
data: [{"text": "Known Author 1", "id": 1}, {"text": "Known Author 2", "id": 2}]
});
$(".js-example-tags").on('select2:select', register);


When the user hits return:


  1. Select2 is supposed to add the new option with value = text as placeholder and

  2. trigger the
    select2:select
    event and thus the
    register
    function,

  3. whose AJAX call will (hopefully) return the newly created object ID,

  4. which replaces the previous option value with the real one.



But evidently, somehow something else happens: The first tag is added correctly – at least from the visual appearance – but when you add a second tag, it vanishes upon pressing return, and from the third tag onward, new tags overwrite the first tag.

The Select2 documentation is minimal on this point, so it’s likely a usage error. Thank you!

Answer

New Solution

I’ve found [this issue comment](https://github.com/select2/select2/issues/3057#issuecomment-77560623 ) in the Select2 bug tracker after the old solution sort of stopped working in some cases. I don’t know why the selected: true attribute used to work for me; maybe it just seemed to work by accident. (New Fiddle.)

function selectOption(select, id) {
  var selectedValues = select.val();
  if (selectedValues == null) {
    selectedValues = new Array();
  }
  selectedValues.push(id);
  select.val(selectedValues).trigger('change');
}

function register(event) {
  if (typeof variable !== event.params &&
      event.params.data.text == event.params.data.id) {
    $.ajax({
      type: 'POST',
      url: 'https://www.random.org/integers/?num=1&min=0&max=999&col=1&base=10&format=plain&rnd=new',
      success: function(data) {
        var select = $(event.target);
        var id = data.replace(/^\s+|\s+$/g, '');
        var text = event.params.data.text;
        select.find('option[data-select2-tag="true"][value="' + text + '"]').remove();
        select.append('<option value="' + id + '">' + text + '</option>');
        selectOption(select, id);
      }
    });
  }
}

var select = $(".js-example-tags");
select.select2({
  tags: true,
  // selectOnClose: true,  // Too much recursion error
  data: [{"text": "Known Author 1", "id": 1}, {"text": "Known Author 2", "id": 2}]
});
select.on('select2:select', register);

Bonus question: What’s so special about the word “is” that it always gets stripped by Select2? Other typical stop words don’t get stripped.


Old Solution

In the end (or what counts as end so far) I’ve gone with the solution suggested by Dario – completely recreating the Select2 input after every entry (Fiddle):

function initSelect2(select) {
  select.find('option[data-select2-tag="true"]').remove();
  select.select2({
    tags: true,
    // selectOnClose: true,  // Too much recursion error
    data: select.data('entries')
  });
}

function register(event) {
  if (typeof variable !== event.params &&
      event.params.data.text == event.params.data.id) {
    var model = $(event.target).data('model');
    $.ajax({
      type: 'POST',
      url: 'https://www.random.org/integers/?num=1&min=0&max=999&col=1&base=10&format=plain&rnd=new',
      success: function(data) {
        var select = $(event.target);
        var entries = select.data('entries');
        entry = {id: data.replace(/^\s+|\s+$/g, ''),
                 text: event.params.data.text,
                 selected: true};
        entries.push(entry);
        select.data('entries', entries);
        initSelect2(select);
        select.parent().find('input.select2-search__field').focus();
      }
    });
  }
}

var select = $(".js-example-tags");
select.data('entries', [{"text": "Known Author 1", "id": 1}, {"text": "Known Author 2", "id": 2}]);
initSelect2(select);
select.on('select2:select', register);

Two problems I ran into were:

  1. that the field loses its focus, which needs to be placed back into the input field Select2 generates, and
  2. that the reinitialization retains the data-select2-tag fields (with the placeholder ID) in addition to the newly added field with the proper ID, so that the first need to be cleaned out before Select2 is recreated.

These are solved in the above code.

Comments