Browse Source

Support duplicate options

Now that the data objects have the `element` property, we can test
to make sure it's a DOM element and then use it for selecting the
option. This allows us to select multiple options with the same id,
as well as handle cases where that is already happening.

You cannot use `$e.val()` to select two options with the same id,
as jQuery will reject it, but you can set the second option to
`.selected = true`, which is supported.
Kevin Brown 10 years ago
parent
commit
0bc73941fe

+ 17 - 0
dist/js/select2.amd.full.js

@@ -2153,6 +2153,15 @@ define('select2/data/select',[
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
     var self = this;
     var self = this;
 
 
+    // If data.element is a DOM nose, use it instead
+    if ($(data.element).is('option')) {
+      data.element.selected = true;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     if (this.$element.prop('multiple')) {
     if (this.$element.prop('multiple')) {
       this.current(function (currentData) {
       this.current(function (currentData) {
         var val = [];
         var val = [];
@@ -2187,6 +2196,14 @@ define('select2/data/select',[
       return;
       return;
     }
     }
 
 
+    if ($(data.element).is('option')) {
+      data.element.selected = false;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     this.current(function (currentData) {
     this.current(function (currentData) {
       var val = [];
       var val = [];
 
 

+ 17 - 0
dist/js/select2.amd.js

@@ -2153,6 +2153,15 @@ define('select2/data/select',[
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
     var self = this;
     var self = this;
 
 
+    // If data.element is a DOM nose, use it instead
+    if ($(data.element).is('option')) {
+      data.element.selected = true;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     if (this.$element.prop('multiple')) {
     if (this.$element.prop('multiple')) {
       this.current(function (currentData) {
       this.current(function (currentData) {
         var val = [];
         var val = [];
@@ -2187,6 +2196,14 @@ define('select2/data/select',[
       return;
       return;
     }
     }
 
 
+    if ($(data.element).is('option')) {
+      data.element.selected = false;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     this.current(function (currentData) {
     this.current(function (currentData) {
       var val = [];
       var val = [];
 
 

+ 17 - 0
dist/js/select2.full.js

@@ -11688,6 +11688,15 @@ define('select2/data/select',[
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
     var self = this;
     var self = this;
 
 
+    // If data.element is a DOM nose, use it instead
+    if ($(data.element).is('option')) {
+      data.element.selected = true;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     if (this.$element.prop('multiple')) {
     if (this.$element.prop('multiple')) {
       this.current(function (currentData) {
       this.current(function (currentData) {
         var val = [];
         var val = [];
@@ -11722,6 +11731,14 @@ define('select2/data/select',[
       return;
       return;
     }
     }
 
 
+    if ($(data.element).is('option')) {
+      data.element.selected = false;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     this.current(function (currentData) {
     this.current(function (currentData) {
       var val = [];
       var val = [];
 
 

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.full.min.js


+ 17 - 0
dist/js/select2.js

@@ -2581,6 +2581,15 @@ define('select2/data/select',[
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
     var self = this;
     var self = this;
 
 
+    // If data.element is a DOM nose, use it instead
+    if ($(data.element).is('option')) {
+      data.element.selected = true;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     if (this.$element.prop('multiple')) {
     if (this.$element.prop('multiple')) {
       this.current(function (currentData) {
       this.current(function (currentData) {
         var val = [];
         var val = [];
@@ -2615,6 +2624,14 @@ define('select2/data/select',[
       return;
       return;
     }
     }
 
 
+    if ($(data.element).is('option')) {
+      data.element.selected = false;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     this.current(function (currentData) {
     this.current(function (currentData) {
       var val = [];
       var val = [];
 
 

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.min.js


+ 17 - 0
src/js/select2/data/select.js

@@ -30,6 +30,15 @@ define([
   SelectAdapter.prototype.select = function (data) {
   SelectAdapter.prototype.select = function (data) {
     var self = this;
     var self = this;
 
 
+    // If data.element is a DOM nose, use it instead
+    if ($(data.element).is('option')) {
+      data.element.selected = true;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     if (this.$element.prop('multiple')) {
     if (this.$element.prop('multiple')) {
       this.current(function (currentData) {
       this.current(function (currentData) {
         var val = [];
         var val = [];
@@ -64,6 +73,14 @@ define([
       return;
       return;
     }
     }
 
 
+    if ($(data.element).is('option')) {
+      data.element.selected = false;
+
+      this.$element.trigger('change');
+
+      return;
+    }
+
     this.current(function (currentData) {
     this.current(function (currentData) {
       var val = [];
       var val = [];
 
 

+ 181 - 0
tests/data/select-tests.js

@@ -154,6 +154,158 @@ test('multiple adds to the old value', function (assert) {
   assert.deepEqual($select.val(), ['default', '2']);
   assert.deepEqual($select.val(), ['default', '2']);
 });
 });
 
 
+test('duplicates - single - same id on select triggers change',
+  function (assert) {
+  var $select = $('#qunit-fixture .duplicates');
+
+  var data = new SelectData($select, data);
+  var second = $('#qunit-fixture .duplicates option')[2];
+
+  var changeTriggered = false;
+
+  assert.equal($select.val(), 'one');
+
+  $select.on('change', function () {
+    changeTriggered = true;
+  });
+
+  data.select({
+    id: 'one',
+    text: 'Uno',
+    element: second
+  });
+
+  assert.equal(
+    $select.val(),
+    'one',
+    'The value never changed'
+  );
+
+  assert.ok(
+    changeTriggered,
+    'The change event should be triggered'
+  );
+
+  assert.ok(
+    second.selected,
+    'The second duplicate is selected, not the first'
+  );
+});
+
+test('duplicates - single - different id on select triggers change',
+  function (assert) {
+  var $select = $('#qunit-fixture .duplicates');
+
+  var data = new SelectData($select, data);
+  var second = $('#qunit-fixture .duplicates option')[2];
+
+  var changeTriggered = false;
+
+  $select.val('two');
+
+  $select.on('change', function () {
+    changeTriggered = true;
+  });
+
+  data.select({
+    id: 'one',
+    text: 'Uno',
+    element: second
+  });
+
+  assert.equal(
+    $select.val(),
+    'one',
+    'The value changed to the duplicate id'
+  );
+
+  assert.ok(
+    changeTriggered,
+    'The change event should be triggered'
+  );
+
+  assert.ok(
+    second.selected,
+    'The second duplicate is selected, not the first'
+  );
+});
+
+test('duplicates - multiple - same id on select triggers change',
+function (assert) {
+  var $select = $('#qunit-fixture .duplicates-multi');
+
+  var data = new SelectData($select, data);
+  var second = $('#qunit-fixture .duplicates-multi option')[2];
+
+  var changeTriggered = false;
+
+  $select.val(['one']);
+
+  $select.on('change', function () {
+    changeTriggered = true;
+  });
+
+  data.select({
+    id: 'one',
+    text: 'Uno',
+    element: second
+  });
+
+  assert.deepEqual(
+    $select.val(),
+    ['one', 'one'],
+    'The value now has duplicates'
+  );
+
+  assert.ok(
+    changeTriggered,
+    'The change event should be triggered'
+  );
+
+  assert.ok(
+    second.selected,
+    'The second duplicate is selected, not the first'
+  );
+});
+
+test('duplicates - multiple - different id on select triggers change',
+function (assert) {
+  var $select = $('#qunit-fixture .duplicates-multi');
+
+  var data = new SelectData($select, data);
+  var second = $('#qunit-fixture .duplicates-multi option')[2];
+
+  var changeTriggered = false;
+
+  $select.val(['two']);
+
+  $select.on('change', function () {
+    changeTriggered = true;
+  });
+
+  data.select({
+    id: 'one',
+    text: 'Uno',
+    element: second
+  });
+
+  assert.deepEqual(
+    $select.val(),
+    ['two', 'one'],
+    'The value has the new id'
+  );
+
+  assert.ok(
+    changeTriggered,
+    'The change event should be triggered'
+  );
+
+  assert.ok(
+    second.selected,
+    'The second duplicate is selected, not the first'
+  );
+});
+
 module('Data adapter - Select - query');
 module('Data adapter - Select - query');
 
 
 test('all options are returned with no term', function (assert) {
 test('all options are returned with no term', function (assert) {
@@ -258,3 +410,32 @@ test('empty optgroups are still shown when queried', function (assert) {
     );
     );
   });
   });
 });
 });
+
+test('multiple options with the same value are returned', function (assert) {
+  var $select = $('#qunit-fixture .duplicates');
+
+  var data = new SelectData($select, options);
+
+  data.query({}, function (data) {
+    assert.equal(
+      data.length,
+      3,
+      'The duplicate option should still be returned when queried'
+    );
+
+    var first = data[0];
+    var duplicate = data[2];
+
+    assert.equal(
+      first.id,
+      duplicate.id,
+      'The duplicates should have the same id'
+    );
+
+    assert.notEqual(
+      first.text,
+      duplicate.text,
+      'The duplicates do not have the same text'
+    );
+  });
+});

+ 12 - 0
tests/data/select.html

@@ -26,6 +26,18 @@
         </optgroup>
         </optgroup>
         <optgroup label="Empty"></optgroup>
         <optgroup label="Empty"></optgroup>
       </select>
       </select>
+
+      <select class="duplicates">
+        <option value="one">One</option>
+        <option value="two">Two</option>
+        <option value="one">Uno</option>
+      </select>
+
+      <select class="duplicates-multi" multiple="multiple">
+        <option value="one">One</option>
+        <option value="two">Two</option>
+        <option value="one">Uno</option>
+      </select>
     </div>
     </div>
 
 
     <script src="../vendor/qunit-1.14.0.js" type="text/javascript"></script>
     <script src="../vendor/qunit-1.14.0.js" type="text/javascript"></script>

Some files were not shown because too many files changed in this diff