Knockout的“with”绑定会丢失包含绑定的事件

时间:2014-02-06 16:52:07

标签: knockout.js

环顾四周后,我看到很多人处理Knockout处理with绑定方式所引起的问题,特别是在应用with绑定的元素内删除和替换DOM元素时。我在Prevent "with" binding from removing DOM elements (Knockout.js)看到一个案例,在Knockout.js Using "with" Binding woes看到一个相关问题。我甚至在https://github.com/mbest/knockout/issues/9https://github.com/knockout/knockout/pull/476跟踪了有关此问题区域的一些讨论。

我了解到从2.2.0开始的版本有所改进,但尽管有这些改进,我仍然看到了一个问题。我在http://jsfiddle.net/bluemonkmn/dSp3L/的JSFiddle中演示了它,其代码在下面复制以供快速参考。

<div data-bind="with: sampleObj">
    <button id="bob" data-bind="text:sampleValue"></button>
    <button data-bind="click:$root.update" id="update">Update</button>
</div>

var sample = ko.observable({
    sampleValue: "Demo"
});
ko.applyBindings({
    sampleObj: sample,
    update: function () {
        sample({
            sampleValue: sample().sampleValue + "x"
        });
    }
});
bob.onclick = function () {
    alert("bob");
};

当您点击第一个按钮时,会弹出一个警告。然后单击第二个按钮,它将替换视图模型的值。到现在为止还挺好。但是请注意第一个按钮的事件处理程序已经消失,单击它不再显示消息。

我尝试为“with”绑定创建一个非常便宜的替代品,如下所示:

  ko.bindingHandlers.safeWith = {
     'init': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        return { 'controlsDescendantBindings': true };
     },
     'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var dataValue = ko.utils.unwrapObservable(valueAccessor());
        if (dataValue) {
           childContext = bindingContext.createChildContext(dataValue);
           ko.applyBindingsToDescendants(childContext, element);
        }
     }
  };

这非常适合在with块中保留事件处理程序。但是,我发现每次发生这种情况时,一些事件处理程序会加倍,因为不会被删除。我在http://jsfiddle.net/bluemonkmn/dSp3L/2/

的小提琴的更新版本中证明了这一点

当您单击第一个按钮时,您仍然会收到消息,即使您单击第二个按钮以更新该值,它仍会继续工作。但是,如果再次单击第二个按钮,您会发现每次更新值时,事件处理的次数都会增加一倍。

是否有一种简单的方法来修复此“with”实现。我宁愿不使用knockout.js的分叉副本,因为我希望能够跟上最新版本。理想情况下,我只想要一个像withlight这样的绑定处理程序,它可以独立而不需要knockout.js的分支,并且还可以反映更新的视图模型的值而不会丢失事件处理程序。

澄清:在我的真实代码中显示警报的click事件实际上是一个事件处理程序,它被类似(我相信)的代码附加到jQuery-UI中所见的代码,您只需调用它DOM元素上的初始化函数,它调整DOM并设置事件以使ti像一个很好的控件一样工作。我无法控制如何附加此事件,除非确定何时调用此高级函数来设置所有内容。

1 个答案:

答案 0 :(得分:0)

避免with绑定选项吗?

<div>
    <button id="bob" data-bind="text: sampleObj().sampleValue"></button>
    <button data-bind="click: update" id="update">Update</button>
</div>

<强>更新

看起来像if,ifnot和foreach等其他控制流绑定应该在dom 中绑定到可观察的时添加和删除元素。

所以我想知道让你的孩子语境不可观察是否可行?例如,而不是这个,

var sample = ko.observable({
    sampleValue: "Demo"
});

...尝试更像这样的东西?

var sample = {
    sampleValue: ko.observable('Demo')
};

这样,你的观察者每次都不会被摧毁,我们只会改变这个值。这是小提琴:http://jsfiddle.net/VFKR6/1/

......这就是代码的样子:

<!-- unchanged from the OP -->
<div data-bind="with: sampleObj">
    <button id="bob" data-bind="text:sampleValue"></button>
    <button data-bind="click:$root.update" id="update">Update</button>
</div>

// don't make sample observable, only make sample.sampleValue observable
var sample = {sampleValue: ko.observable("Demo")};
ko.applyBindings({
    sampleObj: sample,
    update: function () { sample.sampleValue(sample.sampleValue() + 'x'); }
});
bob.onclick = function() {alert("bob");};

这样做你想要的,改变子上下文中按钮的文本,它不会破坏bob.onclick事件。