【问题标题】:Knockout binding error when dom element is moved移动 dom 元素时敲除绑定错误
【发布时间】:2018-03-05 22:27:48
【问题描述】:

我有嵌套的自定义敲除绑定。

顶级绑定扩展绑定上下文并添加到它。

子绑定必须是子绑定,因为它使用添加到绑定上下文的数据。

伪代码-

ko.bindingHandlers.map = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {

     let map = new map({element: element});
     let innerBindingContext = bindingContext.extend({"map": map});
     ko.applyBindingsToDescendants(innerBindingContext, element);

     // tell KO *not* to bind the descendants itself, otherwise they will be bound twice
     return { controlsDescendantBindings: true };
    }
};

ko.bindingHandlers.overlay = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {

     let map = bindingContext.map;
     let overlay = new overlay({element: element});
     map.addOverlay(overlay);

     // have tried 1. code below, 
     // 2. createChildContext,
     // 3. not returning anything, 
     // 4. ko.cleanNode(element); prior to binding,
     // all with the same error

     let innerBindingContext = bindingContext.extend({});
     ko.applyBindingsToDescendants(innerBindingContext, element);
     return { controlsDescendantBindings: true };
    }
};

var vm = {
  someVariable: ko.observable("test");
}
ko.applyBindings(vm);

HTML -

<div data-bind="map:{}">
  <div data-bind="overlay:{}">
    <span data-bind="text:someVariable"></span>
  </div>
</div>

无论我做什么,我都会收到错误 -

“您不能对同一个元素多次应用绑定。”

绑定实际上是在使用 javascript 库。该库将 childbinding dom 元素移动到 HTML 中的其他位置。我认为这就是问题所在。

为什么我会因为 dom 元素被移动而收到这个错误,我该如何解决这个问题?

【问题讨论】:

  • 尝试将ko.cleanNode(element); 添加到地图绑定处理程序的最顶部。这可能会从绑定层次结构中删除整个块。
  • @JasonSpake - 我认为这解决了它。它只需要在第三方对象实例化之前添加,必须在元素在 dom 中移动时添加。谢谢!我不明白的是,为什么如果我返回 {controlsdescendantbindings: true} 它会尝试重新绑定。如果您创建答案,我会将其标记为答案
  • 我认为后代绑定命令适用于任何子节点,但顶级元素本身已被主 ko.applyBindings 调用标记为绑定。明天当我在一台真正的计算机上时,我会尝试添加一个更好的答案。

标签: knockout.js


【解决方案1】:

每当通过调用 applyBindings 初始化敲除时,它都会递归地遍历目标元素或文档中的每个节点以查找和应用绑定。我认为可能发生的事情是淘汰赛尝试将绑定应用于每个唯一元素同时移动它们的时间问题。这类似于在循环中修改数组的长度。

当自定义绑定返回 {controlsDescendantBindings: true} 时,它会从主绑定过程中删除所有子节点。 Knockout 将阻止其递归更深入,并且绑定只会在手动完成时应用于这些元素。但是,您的自定义绑定附加到的根元素仍然是该过程的一部分并且已经被绑定(根据定义 - 首先触发自定义绑定)。因此,虽然这有助于解决主要问题,即根元素被绑定,然后移动到 DOM 中的其他位置,以便敲除再次“找到”它并尝试再次绑定它。

在应用第二遍之前使用ko.cleanNode 清除第一遍绑定是一种诊断性黑客攻击。理想情况下,您应该等到元素完成被其他插件移动后才应用绑定。在允许插件移动元素之前允许敲除完成绑定所有内容的情况下,相反的情况也可能起作用,但如果元素被真正删除然后重新添加而不是仅仅被移动,这可能会导致其他问题。这取决于插件的工作方式。

编辑: 如果您无法将插件的时间与绑定分开,另一种可能的解决方案是将地图绑定在包装器元素上。这样,插件所针对的所有元素都可以通过{controlsDescendantBindings: true} 从绑定过程中删除。当插件移动目标节点时,它会留下包装器,每个人都很高兴。 (只要地图绑定不需要对可观察对象的变化做出反应)

ko.bindingHandlers.map = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    let target = $(element).children();
    let map = new Map({element: target});
    let innerBindingContext = bindingContext.extend({"map": map });
    ko.applyBindingsToDescendants(innerBindingContext, target); 

    // tell KO *not* to bind the descendants itself, otherwise they will be bound twice
    return { controlsDescendantBindings: true };
  }
};

【讨论】:

    猜你喜欢
    • 2013-02-10
    • 1970-01-01
    • 2015-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 2013-02-06
    • 1970-01-01
    相关资源
    最近更新 更多