Knockout.js 版本 3.4.0 中断 jQuery 选择的插件绑定

Knockout.js version 3.4.0 breaks jQuery Chosen plugin binding

我在 Durandal 项目工作,这意味着我使用 knockout 库。此外,我使用了 jQuery 选择的插件。

最近,我将knockout从2.0版本升级到3.4.0版本。作为副作用,选择的插件有缺陷。它有一个错误:无论 ko observable 绑定的实际值如何,第一个选择始终是列表中的第一项。

这是一个重现:

ko.bindingHandlers.chosen = {
  init: function (element) {
    ko.bindingHandlers.options.init(element);          
    $(element).chosen({ disable_search_threshold: 10 });
  },
  update: function (element, valueAccessor, allBindings) {
    ko.bindingHandlers.options.update(element, valueAccessor, allBindings);
    $(element).trigger('chosen:updated');
  }
};

ko.applyBindings({ 
  getItemAvailableAccessTypes: function(data) {
    return [
      {codeForChosen: "Open", AccessTypeName: "Open access"},
      {codeForChosen: "Restricted", AccessTypeName: "No access"}
    ];
  },
 child: {
    AccessType: ko.observable("Restricted")
  }
});
.chosen-container { width: 100% !important; }
pre { background: white; padding: 10px; color: #333; font: 11px consolas; border: 1px solid #ddd; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.5.1/chosen.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.5.1/chosen.jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-debug.js"></script>

<div data-bind="with: child">
<select data-bind="chosen: $parent.getItemAvailableAccessTypes($data), value: $data.AccessType,  optionsText: 'AccessTypeName',  optionsValue: 'codeForChosen'"></select>
</div>
<hr>Debug info: <pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

我不熟悉 jQuery.chosen 插件,但我可能有一个修复程序。

我认为 <select> 元素有两种显示其值的方式:

  1. 它在 select 元素本身的 value 属性中,
  2. 并且可以有一个 option 绑定与 selected 属性(通常表示初始 selection)。

我注意到 knockout 没有设置 selected 属性。当我尝试插件是否能够正确设置初始 selection 并显示 selected 时,结果是

现在的挑战是在最初的 selected 选项中包含 selected 属性。我认为 init 方法将是最好的地方,但事实证明默认绑定不会创建选项元素直到 update... It even deletes any elements that are already there.

所以,最终,我将逻辑放在 update 绑定中,一切似乎都按预期工作。

请注意,您可能想要做一些进一步的测试。此外,我相信您需要 return 原始绑定的 return 值,因为这可以让 knockout 知道后代绑定已得到处理。此外,您可能需要检查 custom disposal logic 以确保一旦 select 从 DOM.

中删除,您的 jQuery 小部件就会被销毁

ko.bindingHandlers.chosen = {
  init: function(element) {
    var optionsReturnValue = ko.bindingHandlers.options.init(element);

    $(element).chosen({
      disable_search_threshold: 10
    });

    return optionsReturnValue;
  },
  update: function(element, valueAccessor, allBindings) {
    ko.bindingHandlers.options.update(element, valueAccessor, allBindings);

    var currentValue = ko.unwrap(allBindings.get("value"));
    var selectedOption = element.querySelector("[value=" + currentValue + "]");
    if (selectedOption) {
      selectedOption.setAttribute("selected", "selected");
    }
    
    $(element).trigger('chosen:updated');
  }
};

ko.applyBindings({
  getItemAvailableAccessTypes: function(data) {
    return [{
      codeForChosen: "Open",
      AccessTypeName: "Open access"
    }, {
      codeForChosen: "Restricted",
      AccessTypeName: "No access"
    }];
  },
  child: {
    AccessType: ko.observable("Restricted")
  }
});
.chosen-container {
  width: 100% !important;
}
pre {
  background: white;
  padding: 10px;
  color: #333;
  font: 11px consolas;
  border: 1px solid #ddd;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.5.1/chosen.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.5.1/chosen.jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-debug.js"></script>

<div data-bind="with: child">
  <select data-bind="chosen: $parent.getItemAvailableAccessTypes($data), value: $data.AccessType,  optionsText: 'AccessTypeName',  optionsValue: 'codeForChosen'"></select>
</div>
<hr>Debug info: <pre data-bind="text: ko.toJSON($root, null, 2)"></pre>