Skip to content

Commit 956ac46

Browse files
committed
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 select2#3022.
1 parent a954bae commit 956ac46

12 files changed

Lines changed: 403 additions & 24 deletions

File tree

Gruntfile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = function (grunt) {
1414

1515
'select2/compat/matcher',
1616
'select2/compat/initSelection',
17+
'select2/compat/inputData',
1718
'select2/compat/query',
1819

1920
'select2/dropdown/attachContainer',

dist/js/select2.amd.full.js

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,7 +2406,6 @@ define('select2/data/select',[
24062406
var val = data.id;
24072407

24082408
this.$element.val(val);
2409-
24102409
this.$element.trigger('change');
24112410
}
24122411
};
@@ -2492,6 +2491,10 @@ define('select2/data/select',[
24922491
});
24932492
};
24942493

2494+
SelectAdapter.prototype.addOptions = function ($options) {
2495+
this.$element.append($options);
2496+
};
2497+
24952498
SelectAdapter.prototype.option = function (data) {
24962499
var option;
24972500

@@ -2632,7 +2635,7 @@ define('select2/data/array',[
26322635

26332636
ArrayAdapter.__super__.constructor.call(this, $element, options);
26342637

2635-
$element.append(this.convertToOptions(data));
2638+
this.addOptions(this.convertToOptions(data));
26362639
}
26372640

26382641
Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -2643,7 +2646,7 @@ define('select2/data/array',[
26432646
if ($option.length === 0) {
26442647
$option = this.option(data);
26452648

2646-
this.$element.append($option);
2649+
this.addOptions([$option]);
26472650
}
26482651

26492652
ArrayAdapter.__super__.select.call(this, data);
@@ -2871,7 +2874,7 @@ define('select2/data/tags',[
28712874
var $option = self.option(tag);
28722875
$option.attr('data-select2-tag', true);
28732876

2874-
self.$element.append($option);
2877+
self.addOptions([$option]);
28752878

28762879
self.insertTag(data, tag);
28772880
}
@@ -4057,6 +4060,15 @@ define('select2/options',[
40574060
}
40584061

40594062
this.options = Defaults.apply(this.options);
4063+
4064+
if ($element && $element.is('input')) {
4065+
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
4066+
4067+
this.options.dataAdapter = Utils.Decorate(
4068+
this.options.dataAdapter,
4069+
InputCompat
4070+
);
4071+
}
40604072
}
40614073

40624074
Options.prototype.fromElement = function ($e) {
@@ -4974,6 +4986,112 @@ define('select2/compat/initSelection',[
49744986
return InitSelection;
49754987
});
49764988

4989+
define('select2/compat/inputData',[
4990+
'jquery'
4991+
], function ($) {
4992+
function InputData (decorated, $element, options) {
4993+
this._currentData = [];
4994+
this._valueSeparator = options.get('valueSeparator') || ',';
4995+
4996+
decorated.call(this, $element, options);
4997+
}
4998+
4999+
InputData.prototype.current = function (_, callback) {
5000+
function getSelected (data, selectedIds) {
5001+
var selected = [];
5002+
5003+
if (data.selected || $.inArray(selectedIds, data.id) !== -1) {
5004+
selected.push(data);
5005+
}
5006+
5007+
if (data.children) {
5008+
selected.push.apply(selected, getSelected(data.children, selectedIds));
5009+
}
5010+
5011+
return selected;
5012+
}
5013+
5014+
var selected = [];
5015+
5016+
for (var d = 0; d < this._currentData.length; d++) {
5017+
var data = this._currentData[d];
5018+
5019+
selected.push.apply(
5020+
selected,
5021+
getSelected(
5022+
data,
5023+
this.$element.val().split(
5024+
this._valueSeparator
5025+
)
5026+
)
5027+
);
5028+
}
5029+
5030+
callback(selected);
5031+
};
5032+
5033+
InputData.prototype.select = function (_, data) {
5034+
if (!this.options.get('multiple')) {
5035+
this.current(function (allData) {
5036+
$.map(allData, function (data) {
5037+
data.selected = false;
5038+
});
5039+
});
5040+
}
5041+
5042+
data.selected = true;
5043+
5044+
this._syncValue();
5045+
};
5046+
5047+
InputData.prototype.unselect = function (_, data) {
5048+
data.selected = false;
5049+
5050+
this._syncValue();
5051+
};
5052+
5053+
InputData.prototype._syncValue = function () {
5054+
var self = this;
5055+
5056+
this.current(function (allData) {
5057+
self.$element.val(
5058+
allData.join(
5059+
self._valueSeparator
5060+
)
5061+
);
5062+
self.$element.trigger('change');
5063+
});
5064+
};
5065+
5066+
InputData.prototype.query = function (_, params, callback) {
5067+
var results = [];
5068+
5069+
for (var d = 0; d < this._currentData.length; d++) {
5070+
var data = this._currentData[d];
5071+
5072+
var matches = this.matches(params, data);
5073+
5074+
if (matches !== null) {
5075+
results.push(matches);
5076+
}
5077+
}
5078+
5079+
callback({
5080+
results: results
5081+
});
5082+
};
5083+
5084+
InputData.prototype.addOptions = function (_, $options) {
5085+
var options = $.map($options, function ($option) {
5086+
return $.data($option[0], 'data');
5087+
});
5088+
5089+
this._currentData.push.apply(this._currentData, options);
5090+
};
5091+
5092+
return InputData;
5093+
});
5094+
49775095
define('select2/compat/query',[
49785096

49795097
], function () {

dist/js/select2.amd.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,7 +2406,6 @@ define('select2/data/select',[
24062406
var val = data.id;
24072407

24082408
this.$element.val(val);
2409-
24102409
this.$element.trigger('change');
24112410
}
24122411
};
@@ -2492,6 +2491,10 @@ define('select2/data/select',[
24922491
});
24932492
};
24942493

2494+
SelectAdapter.prototype.addOptions = function ($options) {
2495+
this.$element.append($options);
2496+
};
2497+
24952498
SelectAdapter.prototype.option = function (data) {
24962499
var option;
24972500

@@ -2632,7 +2635,7 @@ define('select2/data/array',[
26322635

26332636
ArrayAdapter.__super__.constructor.call(this, $element, options);
26342637

2635-
$element.append(this.convertToOptions(data));
2638+
this.addOptions(this.convertToOptions(data));
26362639
}
26372640

26382641
Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -2643,7 +2646,7 @@ define('select2/data/array',[
26432646
if ($option.length === 0) {
26442647
$option = this.option(data);
26452648

2646-
this.$element.append($option);
2649+
this.addOptions([$option]);
26472650
}
26482651

26492652
ArrayAdapter.__super__.select.call(this, data);
@@ -2871,7 +2874,7 @@ define('select2/data/tags',[
28712874
var $option = self.option(tag);
28722875
$option.attr('data-select2-tag', true);
28732876

2874-
self.$element.append($option);
2877+
self.addOptions([$option]);
28752878

28762879
self.insertTag(data, tag);
28772880
}
@@ -4057,6 +4060,15 @@ define('select2/options',[
40574060
}
40584061

40594062
this.options = Defaults.apply(this.options);
4063+
4064+
if ($element && $element.is('input')) {
4065+
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
4066+
4067+
this.options.dataAdapter = Utils.Decorate(
4068+
this.options.dataAdapter,
4069+
InputCompat
4070+
);
4071+
}
40604072
}
40614073

40624074
Options.prototype.fromElement = function ($e) {

0 commit comments

Comments
 (0)