Select2 allowclear and knockout.js

Select2 allowclear and knockout.js

本文关键字:js knockout and allowclear Select2      更新时间:2023-09-26

我使用这个简单的绑定select2和knockoutJs:

ko.bindingHandlers.select2 = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var options = ko.toJS(valueAccessor()) || {};
        setTimeout(function () {
            $(element).select2(options);
        }, 0);
    }
};

标记:

<select class="select2" style="width:100%" data-bind="optionsCaption: '',options: $root.items,optionsText: 'description',optionsValue: 'id', value: $root.selectedItem,select2: { placeholder: 'Select an item...',allowClear: true }"></select>

它工作!现在我在Select2中启用allowClear选项,以清除下拉到占位符值,如Select an item...

如果我点击x图标下拉正确设置占位符,但淘汰不更新可观察的绑定值!

我想我必须改变自定义绑定,添加这样的东西:

setTimeout(function () {
    $(element).select2(options).on("select2-removed", function (e) {
       ko.bindingHandlers.value.update(element, function () { return ''; });
    });
...

但是行不通!

有几个问题。

1)在bindinghandler的update更新DOM基于值的变化,你不应该定义一个update回调与能力改变你的模型。

正确的方法是,当定义一个新的bindinghandler时,使用init回调将所有change事件与模型挂钩,update回调仅仅是一个DOM绘制例程

如果您的init提供DOM绘图(如select2提供),您不需要定义update回调。

因此ko.bindingHandlers.value.update(element, function () { return ''; });行只重新绘制DOM,它不做您想要的。

2)您创建的select2绑定有一些孔。

  • 首先,value绑定不知道select2绑定的存在,这就是你挣扎的地方。
  • 第二,您的select2绑定必须等待其他绑定(options绑定)完成DOM创建,这就是为什么您使用setTimeout。但是ko提供了一种定义绑定序列的方法,看看ko value绑定的源代码,它被定义为'after': ['options', 'foreach']
  • 第三,你的select2不响应外部的变化。例如,如果你有另一个UI来更新$root.selectedItem(一个普通的选择列表),由该UI引发的更改将不会同步回你的select2。
解决方案

基于现有的value绑定定义select2绑定(刚发现不需要),并连接所有更改事件

    我们不需要"select2-removed"事件,我们只需要"change"事件。
  • select2提供了所有的图纸,我们不需要update回调。
  • 使用shouldIgnore标志来打破value subscriber和select2 change event handler之间的循环。
罢工罢工

<> http://jsfiddle.net/huocp/8N3zX/6/http://jsfiddle.net/huocp/8N3zX/9/

ko.bindingHandlers.valueSelect2 = {
    'after': ['options'],
    'init': function(element, valueAccessor, allBindings) {
        // kind of extend value binding
        // ko.bindingHandlers.value.init(element, valueAccessor, allBindings);
        var options = allBindings.get('select2Options') || {};
        $(element).select2(options);
        var value = valueAccessor();
        // init val
        $(element).val(ko.unwrap(value)).trigger("change");
        var changeListener;
        if (ko.isObservable(value)) {
            var shouldIgnore = false;
            changeListener = value.subscribe(function(newVal) {
                if (! shouldIgnore) {
                  shouldIgnore = true;
                  // this is just select2 syntax
                  $(element).val(newVal).trigger("change");
                  shouldIgnore = false;
                }
            });
            // this demo only works on single select.
            $(element).on("change", function(e) {
                if (! shouldIgnore) {
                    shouldIgnore = true;
                    if (e.val == '') {
                        // select2 use empty string for unselected value
                        // it could cause problem when you really want '' as a valid option
                        value(undefined);
                    } else {
                        value(e.val);
                    }
                    shouldIgnore = false;
                }
            });
        }
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            if (changeListener) changeListener.dispose();
            $(element).select2("destory");
        });
    }
};