Search code examples
knockout.jsjquery-select2knockout-3.0

Knockouts and dynamic Selects2s with tags and multiples


I am having issues getting the JQuery Select2 with multiples and tags work together with KnockoutJS.

So far I have this working correctly with a traditional Select box, see this jsfiddle.

Also working OK when using Select2 with singleton options, see this jsfiddle.

But as soon as I introduce tags and multi-select the whole thin falls apart, see this jsfiddle

Can you please help me out bind it properly in the last jsfiddle and print out the multiple selected values on the right?

here is the html code of the last example:

<table data-bind="foreach: fLines">
  <tr>
    <td>
      <select style="width:150px;" multiple="true" class="js-example-basic-multiple" data-bind="options: $root.formatValues, value: type"></select>
    </td>
    <td>
      <a href="#" data-bind='click: function() { $root.fLines.remove($data); }'>Remove</a>
    </td>
    <td>
      Select value is: <span data-bind="text: type"></span>
    </td>
  </tr>
</table>
<button style="background-color: transparent; border: none;" data-bind="click: addfLine">Add Format</button>

and the JS for it:

$(document).ready(function() {
  $('.js-example-basic-multiple').select2({
    tags: true
  });
});

var Item = function(format) {
  var self = this;
  self.type = ko.observable(format);
  self.value1 = ko.observable();
};

var Formatters = {
  formatValues: ko.observableArray(["A", "B", "C"]),
  fLines: ko.observableArray([new Item("C")]),
  addfLine: function() {
    this.fLines.push(new Item("C"));
    $('.js-example-basic-single').select2({
      tags: true
    });
  },
  removefLline: function() {
    this.fLines.remove(ko.dataFor(this));
  }
};

ko.applyBindings(Formatters);

Solution

  • I'm not sure what your intention is for the template so I'm going to bypass that for now. In order to reflect multiple selected values you need to change the binding from "value" to "selectedOptions". Then the bind target needs to be an observableArray so in this example I'm using a new property on Item called selectedTypes to hold which types have been selected.

    <select style="width:150px;" multiple="true" class="js-example-basic-single" 
        data-bind="options: $root.formatValues, selectedOptions: selectedTypes"></select>
    

    //var formatValues = ["A", "B", "C"];
    $(document).ready(function() {
      $('.js-example-basic-single').select2({
        tags: true,
        tokenSeparators: [','],
        placeholder: "Add your tags here"
      });
    });
    
    var Item = function(format) {
      var self = this;
      self.type = ko.observable(format);
      self.selectedTypes = ko.observableArray([]);
    	self.value1 = ko.observable();
    };
    
    var Formatters = {
      formatValues: ko.observableArray(["A", "B", "C"]),
      fLines: ko.observableArray([new Item("C")]),
      addfLine: function() {
        this.fLines.push(new Item("C"));
      },
      removefLline: function() {
        this.fLines.remove(ko.dataFor(this));
      }
    };
    
    ko.applyBindings(Formatters);
    td {
      border: 1px dotted blue;
      padding: 2px; 8px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.css" rel="stylesheet"/>
    
    <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>
    
    <table data-bind="foreach: fLines">
      <tr>
        <td>
          <select style="width:150px;" multiple="true" class="js-example-basic-single" data-bind="options: $root.formatValues, selectedOptions: selectedTypes"></select>
        </td>
        <td data-bind="template: type"></td>
        <td>
          <a href="#" data-bind='click: function() { $root.fLines.remove($data); }'>Remove</a>
        </td>
        <td>
          --> value is: <span data-bind="text: ko.toJSON(selectedTypes)"></span>
        </td>
      </tr>
    </table>
    <button style="background-color: transparent; border: none;" data-bind="click: addfLine">Add Format</button>
    
    <script id="A" type="text/html">
      <input data-bind="value: value1" />
    </script>
    
    <script id="B" type="text/html">
      <span>removes leading and trailing spaces</span>
    </script>
    
    <script id="C" type="text/html">
    	Template C
    </script>