1
0
mirror of synced 2025-02-09 16:49:24 +03:00

Merge pull request #3051 from select2/issue_3022

Added compatibility with `<input type="text" />` tags
This commit is contained in:
Kevin Brown 2015-02-27 18:47:13 -05:00
commit 3630385cf7
15 changed files with 627 additions and 24 deletions

View File

@ -14,6 +14,7 @@ module.exports = function (grunt) {
'select2/compat/matcher',
'select2/compat/initSelection',
'select2/compat/inputData',
'select2/compat/query',
'select2/dropdown/attachContainer',

View File

@ -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,134 @@ define('select2/compat/initSelection',[
return InitSelection;
});
define('select2/compat/inputData',[
'jquery'
], function ($) {
function InputData (decorated, $element, options) {
this._currentData = [];
this._valueSeparator = options.get('valueSeparator') || ',';
if ($element.prop('type') === 'hidden') {
if (console && console.warn) {
console.warn(
'Select2: Using a hidden input with Select2 is no longer ' +
'supported and may stop working in the future. It is recommended ' +
'to use a `<select>` element instead.'
);
}
}
decorated.call(this, $element, options);
}
InputData.prototype.current = function (_, callback) {
function getSelected (data, selectedIds) {
var selected = [];
if (data.selected || $.inArray(data.id, selectedIds) !== -1) {
data.selected = true;
selected.push(data);
} else {
data.selected = false;
}
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;
});
});
this.$element.val(data.id);
this.$element.trigger('change');
} else {
var value = this.$element.val();
value += this._valueSeparator + data.id;
this.$element.val(value);
this.$element.trigger('change');
}
};
InputData.prototype.unselect = function (_, data) {
var self = this;
data.selected = false;
this.current(function (allData) {
var values = [];
for (var d = 0; d < allData; d++) {
var item = allData[d];
if (data.id == item.id) {
continue;
}
values.push(data.id);
}
self.$element.val(values.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 () {

View File

@ -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) {

View File

@ -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,134 @@ define('select2/compat/initSelection',[
return InitSelection;
});
define('select2/compat/inputData',[
'jquery'
], function ($) {
function InputData (decorated, $element, options) {
this._currentData = [];
this._valueSeparator = options.get('valueSeparator') || ',';
if ($element.prop('type') === 'hidden') {
if (console && console.warn) {
console.warn(
'Select2: Using a hidden input with Select2 is no longer ' +
'supported and may stop working in the future. It is recommended ' +
'to use a `<select>` element instead.'
);
}
}
decorated.call(this, $element, options);
}
InputData.prototype.current = function (_, callback) {
function getSelected (data, selectedIds) {
var selected = [];
if (data.selected || $.inArray(data.id, selectedIds) !== -1) {
data.selected = true;
selected.push(data);
} else {
data.selected = false;
}
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;
});
});
this.$element.val(data.id);
this.$element.trigger('change');
} else {
var value = this.$element.val();
value += this._valueSeparator + data.id;
this.$element.val(value);
this.$element.trigger('change');
}
};
InputData.prototype.unselect = function (_, data) {
var self = this;
data.selected = false;
this.current(function (allData) {
var values = [];
for (var d = 0; d < allData; d++) {
var item = allData[d];
if (data.id == item.id) {
continue;
}
values.push(data.id);
}
self.$element.val(values.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 () {

File diff suppressed because one or more lines are too long

20
dist/js/select2.js vendored
View File

@ -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) {

File diff suppressed because one or more lines are too long

View File

@ -1588,6 +1588,38 @@ $.fn.select2.defaults.set("theme", "classic");
</div>
</div>
</section>
<h2 id="input-fallback">
Compatibility with <code>&lt;input type="text" /&gt;</code>
</h2>
<p class="alert alert-warning">
<a href="announcements-4.0.html#hidden-input" class="alert-link">Deprecated in Select2 4.0.</a>
It is now encouraged to use the <code>&lt;select&gt;</code> tag instead.
</p>
<p>
In past versions of Select2, a <code>&lt;select&gt;</code> element could
only be used with a limited subset of options. An
<code>&lt;input type="hidden" /&gt;</code> was required instead, which did
not allow for a graceful fallback for users who did not have JavaScript
enabled. Select2 now supports the <code>&lt;select&gt;</code> element for
all options, so it is no longer required to use <code>&lt;input /&gt;</code>
elements with Select2.
</p>
<dl class="dl-horizontal">
<dt>Adapter</dt>
<dd>
<code title="select2/data/base">DataAdapter</code>
</dd>
<dt>Decorator</dt>
<dd>
<code title="select2/compat/inputData">InputData</code>
</dd>
</dl>
</section>
</div>
<script type="text/javascript">

127
src/js/select2/compat/inputData.js vendored Normal file
View File

@ -0,0 +1,127 @@
define([
'jquery'
], function ($) {
function InputData (decorated, $element, options) {
this._currentData = [];
this._valueSeparator = options.get('valueSeparator') || ',';
if ($element.prop('type') === 'hidden') {
if (console && console.warn) {
console.warn(
'Select2: Using a hidden input with Select2 is no longer ' +
'supported and may stop working in the future. It is recommended ' +
'to use a `<select>` element instead.'
);
}
}
decorated.call(this, $element, options);
}
InputData.prototype.current = function (_, callback) {
function getSelected (data, selectedIds) {
var selected = [];
if (data.selected || $.inArray(data.id, selectedIds) !== -1) {
data.selected = true;
selected.push(data);
} else {
data.selected = false;
}
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;
});
});
this.$element.val(data.id);
this.$element.trigger('change');
} else {
var value = this.$element.val();
value += this._valueSeparator + data.id;
this.$element.val(value);
this.$element.trigger('change');
}
};
InputData.prototype.unselect = function (_, data) {
var self = this;
data.selected = false;
this.current(function (allData) {
var values = [];
for (var d = 0; d < allData; d++) {
var item = allData[d];
if (data.id == item.id) {
continue;
}
values.push(data.id);
}
self.$element.val(values.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;
});

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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) {

View File

@ -15,6 +15,7 @@
<script src="helpers.js" type="text/javascript"></script>
<script src="data/base-tests.js" type="text/javascript"></script>
<script src="data/inputData-tests.js" type="text/javascript"></script>
<script src="data/maximumInputLength-tests.js" type="text/javascript"></script>
<script src="data/maximumSelectionLength-tests.js" type="text/javascript"></script>

View File

@ -0,0 +1,125 @@
module('Data adapters - <input> compatibility');
var $ = require('jquery');
var Options = require('select2/options');
var Utils = require('select2/utils');
var ArrayData = require('select2/data/array');
var InputData = require('select2/compat/inputData');
var InputAdapter = Utils.Decorate(ArrayData, InputData);
test('test that options can be selected', function (assert) {
var options = new Options({
data: [
{
id: 'test',
text: 'Test'
}
]
});
var $element = $('<input />');
var adapter = new InputAdapter($element, options);
adapter.select({
id: 'test'
});
assert.equal(
$element.val(),
'test',
'The id of the item should be the value'
);
});
test('test that options can be unselected', function (assert) {
var options = new Options({
data: [
{
id: 'test',
text: 'Test',
selected: true
}
]
});
var $element = $('<input />');
var adapter = new InputAdapter($element, options);
adapter.unselect({
id: 'test'
});
assert.equal(
$element.val(),
'',
'The id should no longer be in the value'
);
});
test('default values can be set', function (assert) {
expect(4);
var options = new Options({
data: [
{
id: 'test',
text: 'Test'
}
]
});
var $element = $('<input value="test" />');
var adapter = new InputAdapter($element, options);
adapter.current(function (data) {
assert.equal(
data.length,
1,
'There should only be a single selected option'
);
var item = data[0];
assert.equal(item.id, 'test');
assert.equal(item.text, 'Test');
});
assert.equal(
$element.val(),
'test',
'The value should not have been altered'
);
});
test('no default value', function (assert) {
expect(2);
var options = new Options({
data: [
{
id: 'test',
text: 'Test'
}
]
});
var $element = $('<input />');
var adapter = new InputAdapter($element, options);
adapter.current(function (data) {
assert.equal(
data.length,
0,
'There should be no selected options'
);
});
assert.equal(
$element.val(),
'',
'The value should not have been altered'
);
});