snapappointments / bootstrap-select

:rocket: The jQuery plugin that brings select elements into the 21st century with intuitive multiselection, searching, and much more.
https://developer.snapappointments.com/bootstrap-select/
MIT License
9.85k stars 2.72k forks source link

Options and selection are duplicated #2738

Open VeloAddict opened 2 years ago

VeloAddict commented 2 years ago

This bug appeared with 1.14.0-beta3.

image

luis-fss commented 2 years ago

I suspect the bug has something to do with the $('.selectpicker').selectpicker('refresh'); command.

luis-fss commented 2 years ago

Here is a stackblitz to demonstrate the bug

matzexcom commented 2 years ago

I can confirm that the .selectpicker('refresh') call creates a newly refreshed list but doesn't remove the old one.

Commit 04cc1fd5abedc85c449d159d9fa6ea48e2e4a2f7 introduces the bug, but I didn't found why yet.

MMR-19 commented 2 years ago

I believe the problem resides on the buildData method. On version beta2 the following did not produce the duplication bug (lines 1776-1790):

switch (type) {
      case 'data': {
        this.selectpicker.main.data = this.selectpicker.current.data = mainData;
        break;
      }
      case 'load': {
        Array.prototype.push.apply(this.selectpicker.main.data, mainData);
        this.selectpicker.current.data = this.selectpicker.main.data;
        break;
      }
      case 'search': {
        Array.prototype.push.apply(this.selectpicker.search.data, mainData);
        break;
      }
    }

But now on the current version beta3 the following is producing the duplication (lines 1805-1818):

switch (type) {
        case 'data': {
          if (!this.selectpicker.main.data) {
            this.selectpicker.main.data = [];
          }
>         Array.prototype.push.apply(this.selectpicker.main.data, mainData);
>         this.selectpicker.current.data = this.selectpicker.main.data;
          break;
        }
        case 'search': {
          Array.prototype.push.apply(this.selectpicker.search.data, mainData);
          break;
        }
      }

From what I could gather, the line 1810 (signaled with >), where mainData is being pushed to selectpicker.main.data is where the duplication occurs (since selectpicker.main.data already has the items, and that line is pushing the items into the array again)


The following fixes the duplication, but not the selection, neither the text displayed Commenting that line and adding = mainData at the end of the next line seems to solve the duplication issue - basically reverting these 2 lines to the previous beta version. As follows:

switch (type) {
        case 'data': {
          if (!this.selectpicker.main.data) {
            this.selectpicker.main.data = [];
          }
>         //Array.prototype.push.apply(this.selectpicker.main.data, mainData);
>         this.selectpicker.current.data = this.selectpicker.main.data = mainData;
          break;
        }
        case 'search': {
          Array.prototype.push.apply(this.selectpicker.search.data, mainData);
          break;
        }
      }
yankarinRG commented 2 years ago

Related: https://github.com/snapappointments/bootstrap-select/issues/2724

luis-fss commented 2 years ago

@yankarinRG commented on #2724: I am also using the workaround of calling selectpicker('destroy') and then selectpicker(), in the meantime of an official bug fix from the author. It's certainly not the most elegant and probably also optimized solution, but at least it works!

Works for this issue too, thanks.

VeloAddict commented 2 years ago

Can we get a beta release to include this fix?

ben-inqipit commented 2 years ago

An easy workaround is to destroy and recreate selectpicker, after select modifications:

let select = $('select#foo');
select.find('option').remove();    // remove all options
select.append($('<option/>', {
    value: 52,
    text : "new item in empty list",
}));
select.selectpicker('destroy'); // temporary patch!
select.selectpicker();          // temporary patch!
calxibe commented 2 years ago

I needed the $(ele).selectpicker('refresh') to work now without much rewrite of code.

A dirty, but quick fix is to add this into the refresh: function() {} of the source code.

$(this.$element[0]).selectpicker('destroy');
$(this.$element[0]).selectpicker();
return;
mtm2010 commented 2 years ago

I changed it using these codes

$('.selectpicker').selectpicker('destroy'); $('.selectpicker').val('').selectpicker('update');

oprocopio commented 2 years ago

I could fix the duplicated displayed text with the following change:

On line 317 I changed the if statement if (this.options.source.data && !this.multiple && selectedOptions.length > 1) { with if (!this.multiple && selectedOptions.length > 1) {

this.options.source.data was always empty and so the conditions was never true.

I'm still testing but it seems to work.

pc-perrycima commented 1 year ago

Thanks for the patch!

g-pane commented 1 year ago

Where i can find the NPM version with this bug fixed? 1.14.0-beta3 still have this bug, i have reverted to 1.14.0-beta2

MadsonDouglas commented 1 year ago

My solution to the problem is not one of the best in the world, but it did.

basically i replaced this

$.each(my_list, function (index, value) {
    $(`#my-select-id`).append('<option value="' + value.token + '">' +  value.text + "</option>");
})

for this

$.each(my_list, function (index, value) {
    if ($(`#my-select-id option[value='${value.token}']`).text().length == 0) {
       $(`#my-select-id`).append('<option value="' + value.token + '">' +  value.text + "</option>");
    }
});
extraric commented 1 year ago

Although @MiguelMRebocho solve the duplicated option issue, the prevDatadeselection still not working, bc prevOption.liIndex value is nowhere to that.selectpicker.main.data range. (Same to prevIndex suggested by @jayb611 in 2712).

prevData = prevOption ? that.selectpicker.main.data[prevOption.liIndex] : false,

that.selectpicker.main.data is a filtered dataset, without disabled options. How can I pair prevOption ( equal to that.$element[0].options[selectedIndex]) to that? In this picture: it is the 4th, option.op_inprogress, liIndexis 7, but on main.data it should be 1. image

So my solution: skip that prevData search, deselect everything, before new select. Edit line 2794:

  //if (prevData) that.setSelected(prevData, false);
  that.deselectAll(); //insert this
  that.setSelected(clickedData, true);

Disable multi check in line 3634:

  //if (!this.multiple) return;
marekvse commented 1 year ago

An easy workaround is to destroy and recreate selectpicker, after select modifications:

let select = $('select#foo');
select.find('option').remove();    // remove all options
select.append($('<option/>', {
    value: 52,
    text : "new item in empty list",
}));
select.selectpicker('destroy'); // temporary patch!
select.selectpicker();          // temporary patch!

BE AWARE that this also destroys any event bindings on the picker so after re-initializing it you'd have to set them again. So that was not satisfactory for me and looked into the code to try a better fix...

THIS IS WHAT SEEMS TO WORK for both preventing the item as well as selection duplication:

In the "refresh" function I added this.selectpicker.main.data = null; after the "else" in the first conditional statement like so:

if (this.options.source.data) {
    this.render();
    this.buildList();
} else {
    this.selectpicker.main.data = null; // Marek Vsechovsky's fix: Fixes https://github.com/snapappointments/bootstrap-select/issues/2738 (duplication of list items and selections on "refresh").
    this.fetchData(function () {
        that.render();
        that.buildList();
    });
}
rachelcarretero commented 1 year ago

hi there! I'm working with cascade select, with countries, states and cities like this.

image

 // document ready with options do you want ( all selects)
  $('.selectpicker').selectpicker({
        //liveSearch: true,
        liveSearchNormalize: true,
        dropupAuto: true,
        size: 6,

    });

Then on change :

 $("#selectPaises").on("change", function (e) {
        var pais = this.value;
        LoadProvincias(pais);
    });

function LoadProvincias(pais) {
    var select = $("#selectProvincias");
    if (pais > 0) {
        $.getJSON('?handler=Provincias&pais=' + pais + '', (data) => {
            // clear options
            select.find("option").remove();
            // add new options
            $.each(data, function (i, item) {
                select.append("<option  data-tokens='" + item.provinciaKey + "' value='" + item.provinciaId + "'>" + item.provinciaNombre + "</option>");
            });
            // clear + refresh
            select.selectpicker("destroy");
            select.selectpicker("render");

        });
    }
    else {
    }
}

I call to "render" method because select.selectpicker(); lost the preferences . destroy + refrsh dosn't work.

czachor commented 7 months ago

For me, the quickest fix is to destroy and reinit the selectpicker:

$(selector).selectpicker('destroy');
$(selector).selectpicker();

Anyway any chance to fix the issue?

aliml92 commented 7 months ago

For me, the quickest fix is to destroy and reinit the selectpicker:

$(selector).selectpicker('destroy');
$(selector).selectpicker();

Anyway any chance to fix the issue?

@czachor Is it working? I did the same, but it is removing select element altogether.

czachor commented 7 months ago

@aliml92 Yes. Just define selector earlier, eg. let selector = $('#theSelector');.

aliml92 commented 7 months ago

Thank you @czachor. It worked. However when pressing the back button, the select button is becoming unresponsive.

NgoDuy2197 commented 5 months ago
        let certSelect = $("#cert-select")
        certSelect.append($("<option>").attr("data-tokens", "auto").val("auto").text(`AUTO`));
        certSelect.selectpicker()

still not work for me ?

carsanwitt commented 4 months ago

FYI, this was fixed in this PR: https://github.com/snapappointments/bootstrap-select/pull/2816

Since there's no release, we downloaded the non minified latest version from the dev branch and it works: https://github.com/snapappointments/bootstrap-select/blob/dev/js/bootstrap-select.js

vanyapeev94 commented 1 week ago

да, действительно если

  case 'data': {
      if (!this.selectpicker.main.data) {
          this.selectpicker.main.data = [];
      }
      Array.prototype.push.apply(this.selectpicker.main.data, mainData);
      this.selectpicker.current.data = this.selectpicker.main.data;
      break;
  },

заменить на

 case 'data': {
    this.selectpicker.main.data = this.selectpicker.current.data = mainData;
    break;
  }
  case 'load': {
    Array.prototype.push.apply(this.selectpicker.main.data, mainData);
    this.selectpicker.current.data = this.selectpicker.main.data;
    break;
  }

то проблема с refresh решается