Added compatibility with <input />
tags
This adds backwards compatibility back into Select2 for `<input />` tags. The compatibility modules are only available in the full version and will trigger a warning if a hidden input is being used. With the new decorator, Select2 should support the basic operations that it previously did, with the exception of completely overriding the internal objects. As we no longer expose `data` as a writable method, it is no longer possible to completely override the selected data. The state is still managed internally, but in order to prevent data corruption issues in the past, it is not exposed to the public. Some small changes needed to be made to how Select2 was dynamically generating new `<option>` tags, so now a method is called that can be overridden. In the case of the new decorator, this method is intercepted and handled without having to actually place the `<option>` tags into the DOM. The decorator is applied after all of the other defaults, as the defaults are not given the current element. There has only been limited testing with this decorator, primarily using the `data` and `placeholder` options. This closes https://github.com/select2/select2/issues/3022.
This commit is contained in:
parent
a954bae228
commit
956ac46dab
@ -14,6 +14,7 @@ module.exports = function (grunt) {
|
||||
|
||||
'select2/compat/matcher',
|
||||
'select2/compat/initSelection',
|
||||
'select2/compat/inputData',
|
||||
'select2/compat/query',
|
||||
|
||||
'select2/dropdown/attachContainer',
|
||||
|
126
dist/js/select2.amd.full.js
vendored
126
dist/js/select2.amd.full.js
vendored
@ -2406,7 +2406,6 @@ define('select2/data/select',[
|
||||
var val = data.id;
|
||||
|
||||
this.$element.val(val);
|
||||
|
||||
this.$element.trigger('change');
|
||||
}
|
||||
};
|
||||
@ -2492,6 +2491,10 @@ define('select2/data/select',[
|
||||
});
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.addOptions = function ($options) {
|
||||
this.$element.append($options);
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option;
|
||||
|
||||
@ -2632,7 +2635,7 @@ define('select2/data/array',[
|
||||
|
||||
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
||||
|
||||
$element.append(this.convertToOptions(data));
|
||||
this.addOptions(this.convertToOptions(data));
|
||||
}
|
||||
|
||||
Utils.Extend(ArrayAdapter, SelectAdapter);
|
||||
@ -2643,7 +2646,7 @@ define('select2/data/array',[
|
||||
if ($option.length === 0) {
|
||||
$option = this.option(data);
|
||||
|
||||
this.$element.append($option);
|
||||
this.addOptions([$option]);
|
||||
}
|
||||
|
||||
ArrayAdapter.__super__.select.call(this, data);
|
||||
@ -2871,7 +2874,7 @@ define('select2/data/tags',[
|
||||
var $option = self.option(tag);
|
||||
$option.attr('data-select2-tag', true);
|
||||
|
||||
self.$element.append($option);
|
||||
self.addOptions([$option]);
|
||||
|
||||
self.insertTag(data, tag);
|
||||
}
|
||||
@ -4057,6 +4060,15 @@ define('select2/options',[
|
||||
}
|
||||
|
||||
this.options = Defaults.apply(this.options);
|
||||
|
||||
if ($element && $element.is('input')) {
|
||||
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
|
||||
|
||||
this.options.dataAdapter = Utils.Decorate(
|
||||
this.options.dataAdapter,
|
||||
InputCompat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Options.prototype.fromElement = function ($e) {
|
||||
@ -4974,6 +4986,112 @@ define('select2/compat/initSelection',[
|
||||
return InitSelection;
|
||||
});
|
||||
|
||||
define('select2/compat/inputData',[
|
||||
'jquery'
|
||||
], function ($) {
|
||||
function InputData (decorated, $element, options) {
|
||||
this._currentData = [];
|
||||
this._valueSeparator = options.get('valueSeparator') || ',';
|
||||
|
||||
decorated.call(this, $element, options);
|
||||
}
|
||||
|
||||
InputData.prototype.current = function (_, callback) {
|
||||
function getSelected (data, selectedIds) {
|
||||
var selected = [];
|
||||
|
||||
if (data.selected || $.inArray(selectedIds, data.id) !== -1) {
|
||||
selected.push(data);
|
||||
}
|
||||
|
||||
if (data.children) {
|
||||
selected.push.apply(selected, getSelected(data.children, selectedIds));
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
var selected = [];
|
||||
|
||||
for (var d = 0; d < this._currentData.length; d++) {
|
||||
var data = this._currentData[d];
|
||||
|
||||
selected.push.apply(
|
||||
selected,
|
||||
getSelected(
|
||||
data,
|
||||
this.$element.val().split(
|
||||
this._valueSeparator
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
callback(selected);
|
||||
};
|
||||
|
||||
InputData.prototype.select = function (_, data) {
|
||||
if (!this.options.get('multiple')) {
|
||||
this.current(function (allData) {
|
||||
$.map(allData, function (data) {
|
||||
data.selected = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
data.selected = true;
|
||||
|
||||
this._syncValue();
|
||||
};
|
||||
|
||||
InputData.prototype.unselect = function (_, data) {
|
||||
data.selected = false;
|
||||
|
||||
this._syncValue();
|
||||
};
|
||||
|
||||
InputData.prototype._syncValue = function () {
|
||||
var self = this;
|
||||
|
||||
this.current(function (allData) {
|
||||
self.$element.val(
|
||||
allData.join(
|
||||
self._valueSeparator
|
||||
)
|
||||
);
|
||||
self.$element.trigger('change');
|
||||
});
|
||||
};
|
||||
|
||||
InputData.prototype.query = function (_, params, callback) {
|
||||
var results = [];
|
||||
|
||||
for (var d = 0; d < this._currentData.length; d++) {
|
||||
var data = this._currentData[d];
|
||||
|
||||
var matches = this.matches(params, data);
|
||||
|
||||
if (matches !== null) {
|
||||
results.push(matches);
|
||||
}
|
||||
}
|
||||
|
||||
callback({
|
||||
results: results
|
||||
});
|
||||
};
|
||||
|
||||
InputData.prototype.addOptions = function (_, $options) {
|
||||
var options = $.map($options, function ($option) {
|
||||
return $.data($option[0], 'data');
|
||||
});
|
||||
|
||||
this._currentData.push.apply(this._currentData, options);
|
||||
};
|
||||
|
||||
return InputData;
|
||||
});
|
||||
|
||||
define('select2/compat/query',[
|
||||
|
||||
], function () {
|
||||
|
20
dist/js/select2.amd.js
vendored
20
dist/js/select2.amd.js
vendored
@ -2406,7 +2406,6 @@ define('select2/data/select',[
|
||||
var val = data.id;
|
||||
|
||||
this.$element.val(val);
|
||||
|
||||
this.$element.trigger('change');
|
||||
}
|
||||
};
|
||||
@ -2492,6 +2491,10 @@ define('select2/data/select',[
|
||||
});
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.addOptions = function ($options) {
|
||||
this.$element.append($options);
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option;
|
||||
|
||||
@ -2632,7 +2635,7 @@ define('select2/data/array',[
|
||||
|
||||
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
||||
|
||||
$element.append(this.convertToOptions(data));
|
||||
this.addOptions(this.convertToOptions(data));
|
||||
}
|
||||
|
||||
Utils.Extend(ArrayAdapter, SelectAdapter);
|
||||
@ -2643,7 +2646,7 @@ define('select2/data/array',[
|
||||
if ($option.length === 0) {
|
||||
$option = this.option(data);
|
||||
|
||||
this.$element.append($option);
|
||||
this.addOptions([$option]);
|
||||
}
|
||||
|
||||
ArrayAdapter.__super__.select.call(this, data);
|
||||
@ -2871,7 +2874,7 @@ define('select2/data/tags',[
|
||||
var $option = self.option(tag);
|
||||
$option.attr('data-select2-tag', true);
|
||||
|
||||
self.$element.append($option);
|
||||
self.addOptions([$option]);
|
||||
|
||||
self.insertTag(data, tag);
|
||||
}
|
||||
@ -4057,6 +4060,15 @@ define('select2/options',[
|
||||
}
|
||||
|
||||
this.options = Defaults.apply(this.options);
|
||||
|
||||
if ($element && $element.is('input')) {
|
||||
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
|
||||
|
||||
this.options.dataAdapter = Utils.Decorate(
|
||||
this.options.dataAdapter,
|
||||
InputCompat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Options.prototype.fromElement = function ($e) {
|
||||
|
126
dist/js/select2.full.js
vendored
126
dist/js/select2.full.js
vendored
@ -2845,7 +2845,6 @@ define('select2/data/select',[
|
||||
var val = data.id;
|
||||
|
||||
this.$element.val(val);
|
||||
|
||||
this.$element.trigger('change');
|
||||
}
|
||||
};
|
||||
@ -2931,6 +2930,10 @@ define('select2/data/select',[
|
||||
});
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.addOptions = function ($options) {
|
||||
this.$element.append($options);
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option;
|
||||
|
||||
@ -3071,7 +3074,7 @@ define('select2/data/array',[
|
||||
|
||||
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
||||
|
||||
$element.append(this.convertToOptions(data));
|
||||
this.addOptions(this.convertToOptions(data));
|
||||
}
|
||||
|
||||
Utils.Extend(ArrayAdapter, SelectAdapter);
|
||||
@ -3082,7 +3085,7 @@ define('select2/data/array',[
|
||||
if ($option.length === 0) {
|
||||
$option = this.option(data);
|
||||
|
||||
this.$element.append($option);
|
||||
this.addOptions([$option]);
|
||||
}
|
||||
|
||||
ArrayAdapter.__super__.select.call(this, data);
|
||||
@ -3310,7 +3313,7 @@ define('select2/data/tags',[
|
||||
var $option = self.option(tag);
|
||||
$option.attr('data-select2-tag', true);
|
||||
|
||||
self.$element.append($option);
|
||||
self.addOptions([$option]);
|
||||
|
||||
self.insertTag(data, tag);
|
||||
}
|
||||
@ -4496,6 +4499,15 @@ define('select2/options',[
|
||||
}
|
||||
|
||||
this.options = Defaults.apply(this.options);
|
||||
|
||||
if ($element && $element.is('input')) {
|
||||
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
|
||||
|
||||
this.options.dataAdapter = Utils.Decorate(
|
||||
this.options.dataAdapter,
|
||||
InputCompat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Options.prototype.fromElement = function ($e) {
|
||||
@ -5413,6 +5425,112 @@ define('select2/compat/initSelection',[
|
||||
return InitSelection;
|
||||
});
|
||||
|
||||
define('select2/compat/inputData',[
|
||||
'jquery'
|
||||
], function ($) {
|
||||
function InputData (decorated, $element, options) {
|
||||
this._currentData = [];
|
||||
this._valueSeparator = options.get('valueSeparator') || ',';
|
||||
|
||||
decorated.call(this, $element, options);
|
||||
}
|
||||
|
||||
InputData.prototype.current = function (_, callback) {
|
||||
function getSelected (data, selectedIds) {
|
||||
var selected = [];
|
||||
|
||||
if (data.selected || $.inArray(selectedIds, data.id) !== -1) {
|
||||
selected.push(data);
|
||||
}
|
||||
|
||||
if (data.children) {
|
||||
selected.push.apply(selected, getSelected(data.children, selectedIds));
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
var selected = [];
|
||||
|
||||
for (var d = 0; d < this._currentData.length; d++) {
|
||||
var data = this._currentData[d];
|
||||
|
||||
selected.push.apply(
|
||||
selected,
|
||||
getSelected(
|
||||
data,
|
||||
this.$element.val().split(
|
||||
this._valueSeparator
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
callback(selected);
|
||||
};
|
||||
|
||||
InputData.prototype.select = function (_, data) {
|
||||
if (!this.options.get('multiple')) {
|
||||
this.current(function (allData) {
|
||||
$.map(allData, function (data) {
|
||||
data.selected = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
data.selected = true;
|
||||
|
||||
this._syncValue();
|
||||
};
|
||||
|
||||
InputData.prototype.unselect = function (_, data) {
|
||||
data.selected = false;
|
||||
|
||||
this._syncValue();
|
||||
};
|
||||
|
||||
InputData.prototype._syncValue = function () {
|
||||
var self = this;
|
||||
|
||||
this.current(function (allData) {
|
||||
self.$element.val(
|
||||
allData.join(
|
||||
self._valueSeparator
|
||||
)
|
||||
);
|
||||
self.$element.trigger('change');
|
||||
});
|
||||
};
|
||||
|
||||
InputData.prototype.query = function (_, params, callback) {
|
||||
var results = [];
|
||||
|
||||
for (var d = 0; d < this._currentData.length; d++) {
|
||||
var data = this._currentData[d];
|
||||
|
||||
var matches = this.matches(params, data);
|
||||
|
||||
if (matches !== null) {
|
||||
results.push(matches);
|
||||
}
|
||||
}
|
||||
|
||||
callback({
|
||||
results: results
|
||||
});
|
||||
};
|
||||
|
||||
InputData.prototype.addOptions = function (_, $options) {
|
||||
var options = $.map($options, function ($option) {
|
||||
return $.data($option[0], 'data');
|
||||
});
|
||||
|
||||
this._currentData.push.apply(this._currentData, options);
|
||||
};
|
||||
|
||||
return InputData;
|
||||
});
|
||||
|
||||
define('select2/compat/query',[
|
||||
|
||||
], function () {
|
||||
|
5
dist/js/select2.full.min.js
vendored
5
dist/js/select2.full.min.js
vendored
File diff suppressed because one or more lines are too long
20
dist/js/select2.js
vendored
20
dist/js/select2.js
vendored
@ -2845,7 +2845,6 @@ define('select2/data/select',[
|
||||
var val = data.id;
|
||||
|
||||
this.$element.val(val);
|
||||
|
||||
this.$element.trigger('change');
|
||||
}
|
||||
};
|
||||
@ -2931,6 +2930,10 @@ define('select2/data/select',[
|
||||
});
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.addOptions = function ($options) {
|
||||
this.$element.append($options);
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option;
|
||||
|
||||
@ -3071,7 +3074,7 @@ define('select2/data/array',[
|
||||
|
||||
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
||||
|
||||
$element.append(this.convertToOptions(data));
|
||||
this.addOptions(this.convertToOptions(data));
|
||||
}
|
||||
|
||||
Utils.Extend(ArrayAdapter, SelectAdapter);
|
||||
@ -3082,7 +3085,7 @@ define('select2/data/array',[
|
||||
if ($option.length === 0) {
|
||||
$option = this.option(data);
|
||||
|
||||
this.$element.append($option);
|
||||
this.addOptions([$option]);
|
||||
}
|
||||
|
||||
ArrayAdapter.__super__.select.call(this, data);
|
||||
@ -3310,7 +3313,7 @@ define('select2/data/tags',[
|
||||
var $option = self.option(tag);
|
||||
$option.attr('data-select2-tag', true);
|
||||
|
||||
self.$element.append($option);
|
||||
self.addOptions([$option]);
|
||||
|
||||
self.insertTag(data, tag);
|
||||
}
|
||||
@ -4496,6 +4499,15 @@ define('select2/options',[
|
||||
}
|
||||
|
||||
this.options = Defaults.apply(this.options);
|
||||
|
||||
if ($element && $element.is('input')) {
|
||||
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
|
||||
|
||||
this.options.dataAdapter = Utils.Decorate(
|
||||
this.options.dataAdapter,
|
||||
InputCompat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Options.prototype.fromElement = function ($e) {
|
||||
|
4
dist/js/select2.min.js
vendored
4
dist/js/select2.min.js
vendored
File diff suppressed because one or more lines are too long
105
src/js/select2/compat/inputData.js
vendored
Normal file
105
src/js/select2/compat/inputData.js
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
define([
|
||||
'jquery'
|
||||
], function ($) {
|
||||
function InputData (decorated, $element, options) {
|
||||
this._currentData = [];
|
||||
this._valueSeparator = options.get('valueSeparator') || ',';
|
||||
|
||||
decorated.call(this, $element, options);
|
||||
}
|
||||
|
||||
InputData.prototype.current = function (_, callback) {
|
||||
function getSelected (data, selectedIds) {
|
||||
var selected = [];
|
||||
|
||||
if (data.selected || $.inArray(selectedIds, data.id) !== -1) {
|
||||
selected.push(data);
|
||||
}
|
||||
|
||||
if (data.children) {
|
||||
selected.push.apply(selected, getSelected(data.children, selectedIds));
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
var selected = [];
|
||||
|
||||
for (var d = 0; d < this._currentData.length; d++) {
|
||||
var data = this._currentData[d];
|
||||
|
||||
selected.push.apply(
|
||||
selected,
|
||||
getSelected(
|
||||
data,
|
||||
this.$element.val().split(
|
||||
this._valueSeparator
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
callback(selected);
|
||||
};
|
||||
|
||||
InputData.prototype.select = function (_, data) {
|
||||
if (!this.options.get('multiple')) {
|
||||
this.current(function (allData) {
|
||||
$.map(allData, function (data) {
|
||||
data.selected = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
data.selected = true;
|
||||
|
||||
this._syncValue();
|
||||
};
|
||||
|
||||
InputData.prototype.unselect = function (_, data) {
|
||||
data.selected = false;
|
||||
|
||||
this._syncValue();
|
||||
};
|
||||
|
||||
InputData.prototype._syncValue = function () {
|
||||
var self = this;
|
||||
|
||||
this.current(function (allData) {
|
||||
self.$element.val(
|
||||
allData.join(
|
||||
self._valueSeparator
|
||||
)
|
||||
);
|
||||
self.$element.trigger('change');
|
||||
});
|
||||
};
|
||||
|
||||
InputData.prototype.query = function (_, params, callback) {
|
||||
var results = [];
|
||||
|
||||
for (var d = 0; d < this._currentData.length; d++) {
|
||||
var data = this._currentData[d];
|
||||
|
||||
var matches = this.matches(params, data);
|
||||
|
||||
if (matches !== null) {
|
||||
results.push(matches);
|
||||
}
|
||||
}
|
||||
|
||||
callback({
|
||||
results: results
|
||||
});
|
||||
};
|
||||
|
||||
InputData.prototype.addOptions = function (_, $options) {
|
||||
var options = $.map($options, function ($option) {
|
||||
return $.data($option[0], 'data');
|
||||
});
|
||||
|
||||
this._currentData.push.apply(this._currentData, options);
|
||||
};
|
||||
|
||||
return InputData;
|
||||
});
|
4
src/js/select2/data/array.js
vendored
4
src/js/select2/data/array.js
vendored
@ -8,7 +8,7 @@ define([
|
||||
|
||||
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
||||
|
||||
$element.append(this.convertToOptions(data));
|
||||
this.addOptions(this.convertToOptions(data));
|
||||
}
|
||||
|
||||
Utils.Extend(ArrayAdapter, SelectAdapter);
|
||||
@ -19,7 +19,7 @@ define([
|
||||
if ($option.length === 0) {
|
||||
$option = this.option(data);
|
||||
|
||||
this.$element.append($option);
|
||||
this.addOptions([$option]);
|
||||
}
|
||||
|
||||
ArrayAdapter.__super__.select.call(this, data);
|
||||
|
5
src/js/select2/data/select.js
vendored
5
src/js/select2/data/select.js
vendored
@ -61,7 +61,6 @@ define([
|
||||
var val = data.id;
|
||||
|
||||
this.$element.val(val);
|
||||
|
||||
this.$element.trigger('change');
|
||||
}
|
||||
};
|
||||
@ -147,6 +146,10 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.addOptions = function ($options) {
|
||||
this.$element.append($options);
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option;
|
||||
|
||||
|
2
src/js/select2/data/tags.js
vendored
2
src/js/select2/data/tags.js
vendored
@ -71,7 +71,7 @@ define([
|
||||
var $option = self.option(tag);
|
||||
$option.attr('data-select2-tag', true);
|
||||
|
||||
self.$element.append($option);
|
||||
self.addOptions([$option]);
|
||||
|
||||
self.insertTag(data, tag);
|
||||
}
|
||||
|
9
src/js/select2/options.js
vendored
9
src/js/select2/options.js
vendored
@ -11,6 +11,15 @@ define([
|
||||
}
|
||||
|
||||
this.options = Defaults.apply(this.options);
|
||||
|
||||
if ($element && $element.is('input')) {
|
||||
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
|
||||
|
||||
this.options.dataAdapter = Utils.Decorate(
|
||||
this.options.dataAdapter,
|
||||
InputCompat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Options.prototype.fromElement = function ($e) {
|
||||
|
Loading…
Reference in New Issue
Block a user