Diego's comment on the answer given by SpinyMan is important because the empty()
method will remove the select2 instance, so any custom options will no longer be retained. If you want to keep existing select2 options you must save them, destroy the existing select2 instance, and then re-initialize. You can do that like so:
const options = JSON.parse(JSON.stringify(
$('#inputhidden').data('select2').options.options
));
options.data = data;
$('#inputhidden').empty().select2('destroy').select2(options);
I would recommend to always explicitly pass the select2 options however, because the above only copies over simple options and not any custom callbacks or adapters. Also note that this requires the latest stable release of select2 (4.0.13 at the time of this post).
I wrote generic functions to handle this with a few features:
- can handle selectors that return multiple instances
- use the existing select2's instance options (default) or pass in a new set of options
- keep any already-selected values (default) that are still valid, or clear them entirely
function select2UpdateOptions(
selector,
data,
newOptions = null,
keepExistingSelected = true
) {
// loop through all instances of the matching selector and update each instance
$(selector).each(function() {
select2InstanceUpdateOptions($(this), data, newOptions, keepExistingSelected);
});
}
// update an existing select2 instance with new data options
function select2InstanceUpdateOptions(
instance,
data,
newOptions = null,
keepSelected = true
) {
// make sure this instance has select2 initialized
// @link https://select2.org/programmatic-control/methods#checking-if-the-plugin-is-initialized
if (!instance.hasClass('select2-hidden-accessible')) {
return;
}
// get the currently selected options
const existingSelected = instance.val();
// by default use the existing options of the select2 instance unless overridden
// this will not copy over any callbacks or custom adapters however
const options = (newOptions)
? newOptions
: JSON.parse(JSON.stringify(instance.data('select2').options.options))
;
// set the new data options that will be used
options.data = data;
// empty the select and destroy the existing select2 instance
// then re-initialize the select2 instance with the given options and data
instance.empty().select2('destroy').select2(options);
// by default keep options that were already selected;
// any that are now invalid will automatically be cleared
if (keepSelected) {
instance.val(existingSelected).trigger('change');
}
}
UPDATE 2024-04-12: I decided to revisit this answer several years later, after posting a solution to the question How to change placeholder in select2? The most common use case is to keep existing configuration options, but I wanted to revisit the answer and make it a little more flexible, defaulting to overriding configuration options that are passed in rather than replacing them entirely. Otherwise, you have to save the initial options and pass them back into this function.
I also wanted to do some renaming to help clarify the difference between the data options and the select2 configuration options, both in the function names and the variable names. Here is an alternative solution:
// update data options for matching select2 instance(s), default to reselect
// any previously selected options that still exist in the replaced data;
// can also override (default) or replace the select2 configuration options;
function select2UpdateData(
elem,
data,
updatedConfigOptions = {},
keepExistingSelectedData = true,
replaceConfigOptions = false
) {
// loop through all instances of the matching selector and update each instance
toJQuery(elem).each(function() {
select2InstanceUpdateData(
$(this),
data,
updatedConfigOptions,
keepExistingSelectedData,
replaceConfigOptions
);
});
}
// update an individual select2 instance with new data options
function select2InstanceUpdateData(
instance,
data,
updatedConfigOptions = {},
keepExistingSelectedData = true,
replaceConfigOptions = false
) {
// make sure this instance has select2 initialized
if (!instance.hasClass('select2-hidden-accessible')) {
return;
}
// get existing configuration options and the currently-selected data
const existingConfigOptions = JSON.parse(
JSON.stringify(instance.data('select2').options.options)
);
const existingSelected = instance.val();
// by default, keep the original config options and override;
// otherwise if specified, replace the original options entirely
const options = (replaceConfigOptions)
? updatedConfigOptions
: Object.assign({}, existingConfigOptions, updatedConfigOptions)
;
// set the new data options that will be used
options.data = data;
// empty the select and destroy the existing select2 instance,
// then re-initialize the select2 instance
instance.empty().select2('destroy').select2(options);
// by default re-select data that was already selected; any previously-selected
// data that no longer exists will automatically be cleared
if (keepExistingSelectedData) {
instance.val(existingSelected).change();
}
}