New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing Options Dynamically #2830

Closed
abhillman opened this Issue Nov 25, 2014 · 60 comments

Comments

Projects
None yet
@abhillman

abhillman commented Nov 25, 2014

When initializing select2, options can be provided by using the data parameter in the constructor.

element.select2({
    multiple: true,
    placeholder: "Select some options",
    data: ['p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
});

Thereafter, selections (not options) can dynamically be altered by using the data api; for example

element.select2('data', ['x', 'y', 'z']).

Is there any way to dynamically change options?

@kevin-brown

This comment has been minimized.

Member

kevin-brown commented Nov 26, 2014

I thought there was a ticket for this, but I can't seem to find it.

If there is time with Select2 4.0, this is on the list of things that we want to implement.

@kevin-brown kevin-brown added this to the 4.0 milestone Nov 26, 2014

@ova2

This comment has been minimized.

ova2 commented Feb 17, 2015

Is it now possible to change options (add, remove) dynamically? What is the API for that?

@kevin-brown

This comment has been minimized.

Member

kevin-brown commented Feb 18, 2015

It is not currently possible to do this dynamically. We may look into this in the future, but for now it does not look like it will be making it into 4.0.

The general idea of what would need to happen is...

  • Current instance options are retrieved
  • Options are augmented as requested
  • Instance is destroyed
  • Instance is re-initialized with the modified options

Which probably isn't that difficult, but I currently do not have the time to investigate the drawbacks or alternatives.

@kevin-brown kevin-brown removed this from the 4.0 milestone Feb 18, 2015

@ova2

This comment has been minimized.

ova2 commented Feb 19, 2015

I have a working workaround (dirty add-hoc solution, but it works).

var $select = $('#id_of_native_select');

// save current config. options
var options = $select.data('select2').options.options;

// delete all items of the native select element
$select.html('');

// build new items
var items = [];
for (var i = 0; i < ...; i++) {
    // logik to create new items
    ...

    items.push({
        "id": ...,
        "text": ...
    });

    $select.append("<option value=\"" + ... + "\">" + ... + "</option>");
}

// add new items
options.data = items;
$select.select2(options);
@jogaco

This comment has been minimized.

jogaco commented Feb 26, 2015

+1 for a placeholder API

@razakj

This comment has been minimized.

razakj commented Mar 30, 2015

+1 for native API to add/remove/modify items dynamically. Workaround i'm using below -

function initSelect(){
  $("#select").select2({
    ... options ...
  });
}
....
$("#select").select2("destroy");
// possible loop
$("#select").append("<option value='1'>Text</option>");
initSelect();
@benrosati

This comment has been minimized.

benrosati commented May 21, 2015

The nature of dynamically changing content typically requires other methods anyways. I believe Select2 doesn't require a dynamic data method. Rather, a standard (example) added to the documentation of a basic dynamic data change would be a clear enough solution.

Iterating @razakj function-

var data = {
     dataCat1 : [...],
     dataCat2 : [...],
}

function changeData(dataCategory){
    $(".selectDynamic").select2({
        data: dataCategory,
        ...
    });
}

$(".selectStatic").on( "change", function(){
    $(".selectDynamic").select2("destroy");
    $(".selectDynamic").html("<option><option>");
    changeData( data[ $(this).val() ] );
});

changeData(data["dataCat1"]);
@sdhull

This comment has been minimized.

sdhull commented Oct 16, 2015

+1 for this feature request.

Obvious use-case is multiple tagging interfaces on the page, and a user adds a tag to one, it should appear as an option in the others. Destroying & re-creating the select2 instances causes a flash of unstyled content which is not optimal.

We already have something like this, imagine if select2 had this capability:

  window.initSelect2Fields = function () {
    $('[data-simple-select2]').select2();
    $('[data-select2-taggings]').select2({
      tags: true,
      matcher: function (params, data) {
        /* ... custom matcher ... */
      }
    }).on("select2:select", function(e){
      $('[data-select2-taggings]').select2("addOption", e.params.data);
    });
    /* more custom select2 init code */
  }

To implement the suggested workaround (complete with FOSC), I'd have to break out the initialization of tagging interfaces to an inner init function, something like this:

  window.initSelect2Fields = function () {
    $('[data-simple-select2]').select2();
    var initSelect2Taggings = function() {
      $('[data-select2-taggings]').select2({
        tags: true,
        matcher: function (params, data) {
          /* ... custom matcher ... */
        }
      });
    }
    initSelect2Taggings();
    $('[data-simple-select2]').on("select2:select", function(e){
      $('[data-simple-select2]').append("<option id='"+e.params.data.id+"'>"+e.params.data.text+"</option");
      $('[data-simple-select2]').select2("destroy");
      initSelect2Taggings();
    });
    /* ... more custom select2 init code ... */
}

Personally, I'm not sure it's worth the additional maintenance weight.

@Myrdivar

This comment has been minimized.

Myrdivar commented Oct 22, 2015

+1 for this feature. It's common to alter the content of a dropdown dynamically and with the previous verion I had no problem.

@lvecchio

This comment has been minimized.

lvecchio commented Oct 23, 2015

+1 for this feature as well.

@voidale

This comment has been minimized.

voidale commented Oct 31, 2015

+1

@hems

This comment has been minimized.

hems commented Nov 2, 2015

Isn't it possible to make a workaround "query" function? If so, any drawbacks ?

  • Keep the options for each select on javascript objects
  • Don't add the "" to the selects
  • Use the query method to dynamically populate the options when user clicks ( even if zero characters typed )
  • Check the state of the application
  • Render options accordingly ?

I look at the docs but even thought i failed to make it work with the current version of select2, if anybody has an example would be ace.

@IgorDobryn

This comment has been minimized.

IgorDobryn commented Nov 3, 2015

+1

@benoittgt

This comment has been minimized.

benoittgt commented Nov 5, 2015

I've open an issue on StackOverflow, I don't know if it's related. My workaround is ugly, and I would love to fix it.

@renandecarlo

This comment has been minimized.

renandecarlo commented Nov 24, 2015

Just use change().

$('#myselect').html('<option>Whatever</option>').change()
@benoittgt

This comment has been minimized.

benoittgt commented Nov 24, 2015

Thanks @renandecarlo

@40robber

This comment has been minimized.

40robber commented Nov 28, 2015

$('#txtClassTeacher').html("<option value='"+res.source.id+"'></option>");
$('#select2-txtClassTeacher-container').text(res.source.truename);
@mustafaaloko

This comment has been minimized.

mustafaaloko commented Feb 29, 2016

+1

@glowysourworm

This comment has been minimized.

glowysourworm commented Apr 2, 2016

+1 Need a clean way to destroy and recreate. Using $.select2('destroy') then creating on the same element causes bugs to appear in the API. Bad news....

@jvq2

This comment has been minimized.

jvq2 commented Apr 27, 2016

+1 The inability to dynamically change/disable options is definitely a major deal.

@Glideh

This comment has been minimized.

Glideh commented Oct 6, 2016

Your solution destroys the select2 which would like to be avoided (ie: it requires to merge the original config).
People are still having issues since there is no easy way to update the data, like:

$input.val(data).select2('refresh');
// something like that, or even simpler
$input.val(data);
@grunmin

This comment has been minimized.

grunmin commented Nov 4, 2016

My solution (part of the code in a plugin)

    var cmdbSelect2 = function(ele, single){
        var option = {
            language: 'zh-CN',
            width: 'resolve',
        }
        if(single == true){
            $.extend(option,{ multiple: true, maximumSelectionLength: 1 })
        }
        ele.data('cmdbSelect', option)
        return ele.select2(option)
    }

    var selectReOption= function(ele, option){
        var options = ele.data('cmdbSelect')
        ele.data('cmdbSelect', $.extend({}, options, option))
        ele.select2('destroy').select2(ele.data('cmdbSelect'))
    }
@josuelmm

This comment has been minimized.

josuelmm commented Nov 9, 2016

My solution using .get or .post

$('#direccion_integracion').on('change', function (e) {
var id_direccion = $(this).find("option:selected").val();
$.get("catastro/catastro_direccion_integracion.php?id_direccion="+id_direccion, 
function( data ) {
    $(".direccion_integrar").select2("destroy");
    $(".direccion_integrar").html( data );
    $(".direccion_integrar").select2({});
});
});
@Zxurian

This comment has been minimized.

Zxurian commented Dec 15, 2016

so building off of @ova2 's post, I created a jQuery plugin that is supposed to accomplish the same thing

// Function to dynamically set a new set of options to a select2 element
(function($) {
	$.fn.setNewSelect2Data = function(newData) {
		var origOptions = this.data('select2').options.options;
		this.empty().select2($.extend(origOptions, { data: newData }));
		return this;
	};
})(jQuery);

however it appears that this.data('select2').options.options no longer contains the current options that are set on initial initialization. Anyone know the proper way to get the current select2 options at runtime? (Yes, I've tried googling, however any search for "options" has results for select options, not the select2 initialization options.

@Cartman34

This comment has been minimized.

Cartman34 commented Dec 15, 2016

With the last 4.0.3 version of select2, we see a weird behavior in this jsfiddle
At first click, you see that options are reset, minimumInputLength is gone when select2 is called again and at the next step, you see that data are appended to previous ones but other options are resetted.
In this case, we would the opposite, all data are replaced and previous options are kept. But in all cases, the data also set the DOM OPTION elements in the SELECT tag.
I could advise you to use your own function defining the select2 for this element with a data array in argument to always get the same options and only change the data.
You could also see my previous answer.

@gracecarey

This comment has been minimized.

gracecarey commented Dec 15, 2016

+1

@Zxurian

This comment has been minimized.

Zxurian commented Dec 16, 2016

@Cartman34 Your solution doesn't work for data sets that use a structure outside of the fixed { id: 0, text: 'value' } format.

However I'm in agreement that I may have to modify it to make a complete wrapper for select2 initialization as well, instead of just getting current options, unless someone else knows how to make select2 get it's initialization options at runtime.

@trevithj

This comment has been minimized.

trevithj commented Jan 18, 2017

Regards the API for doing dynamic loading: perhaps mimic the DataTables approach and make the ajax option more generic. Current practice seems to take an object that defines a mandatory url:

$('select').select2({
  ajax: { url: '/path/to/search/endpoint' }
});

As I see it, this requires the dynamic data to come from a back-end source. This approach makes a strong coupling between the View and the Model, which in my opinion is not a good practice.
If the ajax option could also be a function, then the dynamic data could come from anywhere:

$('select').select2({
  ajax: function(callbackFn) {
    var newData = getMeSomeNewData();//can be from anywhere: local data or external ajax call.
    callbackFn(newData); //has same effect as the old ajax response
  }
});

A suggestion, for what it's worth. We use DataTables and Select2 a lot, and a consistent approach would be nice. :)

@brunocascio

This comment has been minimized.

brunocascio commented Feb 2, 2017

Which is the status of this?

@emielmolenaar

This comment has been minimized.

emielmolenaar commented Feb 21, 2017

+100

@rborosak

This comment has been minimized.

rborosak commented Feb 22, 2017

+1

1 similar comment
@sannek8552

This comment has been minimized.

sannek8552 commented Feb 22, 2017

+1

@mktcode

This comment has been minimized.

mktcode commented Mar 1, 2017

+1 But can anyone tell me if there is something wrong with @renandecarlo 's solution? Seems to work quite well.

@Zxurian

This comment has been minimized.

Zxurian commented Mar 1, 2017

+1 But can anyone tell me if there is something wrong with @renandecarlo 's solution? Seems to work quite well.

@mktcode There's nothing wrong with it as long as you're using only html <option> tags. It's when you're using data objects that don't have a fixed { id: '', text: ''} that the problem presents itself.

@trevithj

This comment has been minimized.

trevithj commented Mar 2, 2017

@Zxurian that doesn't seem so difficult. An extension of @renandecarlo's solution:

var $select = $('select#some_id');
var newDataObjects = [...whatever...];
var optns = newDataObjects.map(function(item) {
    return '<option value="' + item.someVal + '">' + item.someName + '</option>';
});
$select.html( optns.join("") ).change();
@Zxurian

This comment has been minimized.

Zxurian commented Mar 2, 2017

@trevithj that's still staying with the traditional <option> tags. I forgot to post an addendum which probably would have helped, but with objects, you have the ability to use tempalteSelection, templateResult options, which don't use <option> tags at all, thus calling change() method on the original select doesn't work unless you do some extra data manipulation and transposition, which is just adding extra work, especially when you start dealing with ajax calls.
By having a method on the select2 adapter itself, you can just supply an array of objects of the same schema as the existing objects to it, and it'll use select2's own existing methods (templateSelection, templateResult) without having to write extra code around it, so your total code for updating options is just a single line like the initial poster requested.

newItemList = [
    { itemNum: 23, itemName: 'Shoe', itemShelf: 4, itemBin: 'C' },
    { itemNum: 52, itemName: 'Boot', itemShelf: 9, itemBin: 'A' },
    { itemNum: 88, itemName: 'Laces', itemShelf 2, itemBin: 'E' }
];
// or however you build / get your updated list

// actual code to update select2 options, nothing else needed
mySelect2Object.select2('data', newItemList);
@wreardan

This comment has been minimized.

wreardan commented Mar 14, 2017

This is a major hurdle for me. When implementing shift-click to select all elements, it takes two clicks to refresh and I cannot seem to figure out how to make it trigger instantly for the life of me!!

jQuery(view_field).on("select2:selecting", function(e) { 
	// what you would like to happen
	//console.log('select2 select', event.shiftKey);
	if(shifted) {
		// select all
		$select2 = jQuery(view_field);
		$select2.find('option').attr('selected', true);
		$select2.find('option').trigger('change.select2');
	}
});
@ivofsp

This comment has been minimized.

ivofsp commented Mar 24, 2017

+1
i have one select2 refreshing other select2 like this

function bindOtherSelect2(e) {
//SAVES SELECTED ID
var id = (e.params.data.id);
//AJAX CALL
$.ajax({
url: "/Controller/Method",
type: "POST",
dataType: "json",
data: { id: id },
success: function (result) {
$("#select2Id").html('');
$("#select2Id").select2({ data: result});
}
});
};

I would love to set back the placeholder but i didnt manage to do that, hope this helps someone.

@alenb

This comment has been minimized.

alenb commented Mar 27, 2017

I don't understand why this wasn't labeled as critical, because it's a pretty crucial feature. You should be able to update any options without having to destroy the select2.

@dejan9393

This comment has been minimized.

dejan9393 commented Apr 5, 2017

If you initialise your select2 instance with the data property set, you can use the following to add new items:

var items = [{
    id: 123,
    text: 'Item 1'
}];

var $someDropdown = $('#some_dropdown');

// Clear out existing options
$someDropdown.html('');

var dataAdapter = $someDropdown.data('select2').dataAdapter;
dataAdapter.addOptions(dataAdapter.convertToOptions(items));
@nilov

This comment has been minimized.

nilov commented Apr 7, 2017

The simple jQuery plugin created from this thread:

(function ($) {
    $.fn.refreshDataSelect2 = function (data) {
        this.select2('data', data);

        // Update options
        var $select = $(this[1]);
        var options = data.map(function(item) {
            return '<option value="' + item.id + '">' + item.text + '</option>';
        });
        $select.html(options.join('')).change();
    };
})(jQuery);

Use:

var data = [{ id: 1, text: 'some value' }];
$('.js-some-field').refreshDataSelect2(data);
@not-a-lot

This comment has been minimized.

not-a-lot commented Apr 25, 2017

The simple jQuery plugin created from this thread:

Thank you, nice plugin ! 😃 I had to change your $(this[1]) to $(this[0]), though

@hixel

This comment has been minimized.

hixel commented May 4, 2017

Something like this:

var dataAppleDevices = [{id: 1, text: "iPhone"}, {id: 2, text: "iPad"}, {id: 3, text: "iPod"}];
var dataSamsungDevices = [{id: 1, text: "Galaxy S"}, {id: 2, text: "Galaxy Tab"}, {id: 3, text: "Galaxy A"}];
var select = $("yourSelect");

select.select2({

    placeholder: "Choose your device",
    allowClear: true,
    data: dataAppleDevices 
});

<working...>
select.select2().empty();
select.append("<option></option>"); // for placeholder

select.select2({

    placeholder: "Choose your device",
    allowClear: true,
    data: dataSamsungDevices 
});
@lg134

This comment has been minimized.

lg134 commented Jul 31, 2017

Hello,
Anyone has a workaround that really works for these kinds of case?
I tried all the suggestions but nothing works yet.
I have 2 filters, one by Date, one by Timing. I want to be able to refresh Timing based on the Date chosen. Sounds simple enough, but can't get it to work with Select2. Both get data from arrays.
This is my code:
$( "#time_search" ).select2("destroy").html("");
jQuery( "#time_search" ).append("");
jQuery( "#time_search" ).select2({
placeholder: "Select a timing",
allowClear: true,
data: my_arr

});

I also tried empty():
$( "#time_search" ).select2("destroy").empty();
Didn't work either.

The 2 filters at initialization:
screen shot 2017-07-31 at 10 25 33 pm
After Date div is being selected, and Timing div being refreshed:
screen shot 2017-07-31 at 10 25 51 pm

From developer's tool, I can see that that size of the drop down is reduced to 1px after Select2 being reset. I tried to input an array of string, but the size is still 1px.
screen shot 2017-07-31 at 10 26 31 pm

Any thought how to get this fixed? :(. Love this JS and hope to be able to fix. Thanks a lot.

@isaiahgrant

This comment has been minimized.

isaiahgrant commented Aug 8, 2017

Per documentation found at https://select2.github.io/options.html:

$('select').select2({
  data: [
    {
      id: 'value',
      text: 'Text to display'
    },
    // ... more data objects ...
  ]
});

With that I was able to use something to the effect of:

<select id="my-select" class="select2_single">...</select>
...
// This var would be retrieved from an outside source
var dataArrayFromAJAX = [{ id: 'foo', text: 'bar' }];
$('#my-select').html('').select2({
  data: [ dataArrayFromAJAX ]
});

It replaced existing elements. Or to keep existing options from elements

// Get existing elements
var oldDataArray = [ ];
$('#my-select option').each(function(idx,element) {
    oldDataArray.push( { id: element.value, text: element.innerHTML } );
});
// Merge existing and new elements and refresh select2
$('#my-select').html('').select2({
  data: [ oldDataArray.concat(dataArrayFromAJAX) ]
});
@VuQuang

This comment has been minimized.

VuQuang commented Jan 26, 2018

element.val("");
element.find('option').remove();
$.each(data, function(index, value) {
        element.append('<option value="' + value.id + '">' + value.id  + '</option>');
 });

I do like that. And if u use multiple select2. U can add below line after appending.

element.select2({
         multiple: true,
});

And final event use that.
element.val('').trigger('change');

@alexkiburu

This comment has been minimized.

alexkiburu commented Mar 13, 2018

Cool Stuff @renandecarlo

@GeraElem

This comment has been minimized.

GeraElem commented Mar 13, 2018

Hello
I found this way.

$('#descJugador').select2('val', this.id);
$('#descJugador').select2('data').disabled = false;
$('#descJugador').select2('data', null);

If you do that, the select2 refresh the value dynamically.

@phillanier

This comment has been minimized.

phillanier commented Nov 11, 2018

@dejan9393 wins the day!

If you're using a custom templateSelection and/or templateResult with Ajax data that has custom properties (such as the GitHub repository example in the docs), you can't just add an asdf, because your custom templates will be missing all of the required data!

Here's a slight variation... Adds a single option to the list and selects it. (Note, this works with the AJAX adapter too.)

`var items = {
id: "345", //Important
fullName: "Justin Jones",
title: "Analyst",
country: "Australia"
}

//First, add an option to the select.
var dataAdapter = $('#my-dropdown').data('select2').dataAdapter;
var option = dataAdapter.option(item);
dataAdapter.addOptions(option);

//Set the value of the control and trigger an update
$('#my-dropdown').val(item.id);
$('#my-dropdown').trigger("change");`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment