Knockout 'with binding and select2 in jQuery dialog

Problem:

The select2 jQuery plugin does not work when used in a jQuery dialog box nested in an element that uses with binding bindings. Remove the with binding, and select2 works fine. If with tied to a nested property, then it stops working.

Background:

So, I must have fought for 3 hours, trying to get select2 to work in the jQuery dialog .... talking about pi $$ inging over the notorious wrong tree, I thought it was purely jQuery dialog and choice2. It probably worked right from the start with the _allowInteraction fix. Until I broke the problem down to simple steps, and the reason began to unfold. The problem is with the with binding.

Renouncement

I apologize for working on asinine, which blocks jsFiddle. I also broke up my implementation for illustrative purposes, since the actual model is quite large.

 // models function Department() { this.name = ko.observable('dept1'); this.selectedTeam = ko.observable( new Team() ); } function Team() { this.name = ko.observable('team1'); } function MainModel() { this.department = new Department(); this.showTeam = function() { $('#addTeamDialog').dialog('open'); }; } // setup ko.applyBindings( new MainModel() ); $('#addTeamDialog').dialog({ // fix allow select2 to work on the jq dialog _allowInteraction: function (event) { return !!$(event.target).is(".select2-input") || this._super(event); } }); $('#someList').select2({ data: [ { id: 0, text: 'enhancement' }, { id: 1, text: 'bug' }, { id: 2, text: 'duplicate' }, { id: 3, text: 'invalid' }, { id: 4, text: 'wontfix' } ] }); 
 <link href="http://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet"/> <link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="http://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.full.min.js"></script> <button data-bind="click: showTeam">Add Team</button> <div id="addTeamDialog"> <fieldset data-bind="with: department"> <div class="lite-dialog-field"> <div class="label"> <span data-bind="text: name"></span> </div> <div class="field"> <input type="hidden" id="someList" /> </div> </div> </fieldset> </div> 

Removing data-bind in fieldset and select2 works fine.

When the data-bind parameter on fieldset set to department , select2 works fine.

If the data-bind parameter on fieldset set to department.selectedTeam , select2 does not work.

+5
source share
1 answer

When you are working with Knockout, it is highly recommended that you wrap external libraries such as select2 in bindings. Although you only initialize them once, bindings such as with , template or foreach can change the DOM at any time.

You are in danger either

  • initializing select2 is too early when the knockout hasn't done anything yet, or
  • Knockout discarding and re-rendering markup at a later point, so your select2 is no longer bound anymore

For example, this will happen when Department.selectedTeam is changed.

I found a quick and dirty select2 binding from rniemeyer knockout here . Other than that, I just changed the select2 markup to the standard <select> and made MainModel.department the proper observable to ensure consistency and security.

 ko.bindingHandlers.select2 = { init: function(element, valueAccessor) { var options = ko.toJS(valueAccessor()) || {}; setTimeout(function() { $(element).select2(options); }, 0); } }; // models function Department() { this.name = ko.observable('dept1'); this.selectedTeam = ko.observable( new Team() ); }; function Team() { this.name = ko.observable('team1'); this.values = ["red", "grey", "blue"]; this.selected = ko.observableArray(["blue"]); }; function MainModel() { this.department = ko.observable( new Department() ); this.showTeam = function() { $('#addTeamDialog').dialog('open'); }; }; // setup ko.applyBindings( new MainModel() ); $('#addTeamDialog').dialog({ // fix allow select2 to work on the jq dialog _allowInteraction: function (event) { return !!$(event.target).is(".select2-input") || this._super(event); } }); 
 select { width: 200px; } 
 <link href="http://code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet"/> <link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="http://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.full.min.js"></script> <button data-bind="click: showTeam">Add Team</button> <div id="addTeamDialog"> <fieldset data-bind="with: department().selectedTeam"> <select data-bind="options: values, selectedOptions: selected, select2: { placeholder: 'pick some colors' }"> </select> </fieldset> </div> 
+4
source

All Articles