【问题标题】:Knockout's "with" binding loses events on contained bindingsKnockout 的“with”绑定丢失了包含绑定的事件
【发布时间】:2014-02-06 16:52:07
【问题描述】:

环顾四周后,我看到许多人处理由 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 的分支,而且它还将反映更新后的视图模型的价值,而不会丢失事件处理程序。

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

【问题讨论】:

  • 如果不是 jqueryui,小部件是什么?是剑道吗?什么控制?如果您可以向元素添加绑定,我仍然不清楚您无法控制什么。您可以运行一个工具函数来重新附加点击期间的行为(如果是剑道,我知道它有事件处理程序)。
  • @danludwig 我们有一套基于 jQuery-UI 并与之并行开发的公司控件。我也许能够更新这些控件,但仅仅为了使某些用例具有淘汰赛工作,这不是一个黑客行为。在我看来,更好的“with”实现将是一个更清洁的解决方案。毕竟,“with”本质上是在不必要地删除事件。我想要做的就是引用一个特定的范围,而不必在每个数据绑定属性中包含父变量名。我不希望 DOM 到处移动。
  • 这不完全是这个问题的答案,但与我们的情况非常相关——请参阅stackoverflow.com/q/21415232/78162

标签: knockout.js


【解决方案1】:

是否避免 with 绑定选项?

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

更新

看起来 with 以及其他控制流绑定(例如 if、ifnot 和 foreach)应该在将元素绑定到可观察对象时从 dom 中添加和删除元素

所以我想知道尝试使您的子上下文不可观察是否可行?例如,而不是这个,

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

...试试类似的东西?

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

这样,您的 observable 不会每次都被销毁,我们只是在改变值。这是小提琴: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 事件。

【讨论】:

  • 你没有在问题中提到 jqueryui。也许你应该用真实的代码而不是一个剥离的例子来更新它。
  • 实际代码使用的控件库不是 jQuery-UI,而是基于 jQuery-UI 约定并建立在一些 jQuery-UI 控件之上。我会用这个澄清来更新这个问题,但我认为包含所有真实代码是不切实际的,如果我能解释我正在处理的限制,则应该没有必要。
  • 是的,避免使用 with 绑定可能是一种选择,但没有办法编写类似于 with 绑定的东西,它会产生类似的效果(隐式在每个子元素的没有所有 DOM 重组复杂性的绑定),因为它看起来很简单?
  • 我想我只需要调用每个后代绑定的更新函数,而不是调用 applyBindingsToDescendants。有没有办法做到这一点?
  • 看起来不是这样。即使可以,如果您更改为在生产中使用 ko 缩小版,这也可能会破坏,因为缩小版不会像未缩小版那样暴露整个 api 表面。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-16
  • 2016-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多