Knockout.js v2.3.0 错误“您不能将绑定多次应用于同一元素”

Posted

技术标签:

【中文标题】Knockout.js v2.3.0 错误“您不能将绑定多次应用于同一元素”【英文标题】:Knockout.js v2.3.0 error "You cannot apply bindings multiple times to the same element" 【发布时间】:2013-07-16 15:43:47 【问题描述】:

我刚刚升级到 Knockout.js 2.3.0,现在我收到了这个错误:

You cannot apply bindings multiple times to the same element.

我在 2.2.1 中没有。

单击href 后,我从 MVC 控制器获取部分视图并将其添加到页面中。当我第二次单击链接以获取部分视图时,会发生该错误。我这样做了很多次。

有没有办法清除它并避免抛出新的错误?

这是我的代码:

$.get(url + "GetAssignedCompaniesView?layoutId=" + layoutId + "&noCache=" + new Date().getMilliseconds(), function (result) 
    $("#editAssignedPartial").html($(result));
    showEditAssignedArea(true);
    $(window.document).ready(function () 
        // error is thrown here
        ko.applyBindings(self, window.document.getElementById("editAssigned"));
        $("#layoutId").attr("value", layoutId);
        updateTypeHiddenElement.attr("value", "companies");
    );
);

这是我的 HTML:

<div id="area1">
    <!-- grid here with links -->
</div>
<div id="editAssignedPartial"></div>
$(document).ready(function () 
    'use strict';
    var vm = new Vm();
    ko.applyBindings(vm, document.getElementById("area1"));
);

【问题讨论】:

stopBindings 是更好的方法...knockmeout.net/2012/05/quick-tip-skip-binding.html 【参考方案1】:
ko.cleanNode($("#modalPartialView")[0]);
ko.applyBindings(vm, $("#modalPartialView")[0]);

这对我有用,但正如其他人所说,cleanNode 是一个内部 ko 函数,所以可能有更好的方法。

【讨论】:

【参考方案2】:

我遇到了同样的问题,我是这样解决的:

var vm = new MessagesViewModel()
ko.applyBindings(vm)

function ShowMessagesList() 
   vm.getData("MyParams")


setInterval(ShowMessagesList, 10000)

【讨论】:

【参考方案3】:

在我的例子中,我正在添加一个不存在的元素,或者,我正在向一个可能存在的元素添加绑定,但它的父元素没有。类似这样:

var segDiv =  $("#segments"); // did not exist, wrong id
var theDiv = segDiv.html("<div></div>");

ko.applyBindings(someVM, theDiv);

据我所知,这个错误似乎有点过载,因为它会触发元素可能发生的许多不同错误,比如它不存在。因此,错误描述可能具有高度欺骗性。应该是这样的:

Failure to bind bindings to element. Possible reasons include: multiple binding attempts, element not existing, element not in DOM hierarchy, quirks in browsers, etc

【讨论】:

【参考方案4】:

我在 IE 7/8 中遇到了同样的错误。在包括 IE 9/10 在内的所有其他浏览器中运行良好。

我发现对我有用的是消除自闭标签。

不好:

<div>
    <span data-bind="text: name"/>
</div>

好:

<div>
    <span data-bind="text: name"></span>
</div>

【讨论】:

【参考方案5】:

我遇到此错误的原因不同。

我为希望显示在页面顶部和底部的保存/取消按钮创建了一个模板。起初,当我在 &lt;script type="text/html"&gt; 元素中定义模板时,它起作用了....但后来我听说您可以选择用普通的 &lt;div&gt; 元素创建模板。

(这对我来说效果更好,因为我使用的是 ASP.NET MVC,并且我的 @variableName Razor 语法没有在运行时从 &lt;script&gt; 元素内部执行。所以改为改为 &lt;div&gt;,当页面加载时,我仍然可以让 MVC Razor 引擎在我的 Knockout.js 模板中生成 HTML。)

在我将模板更改为使用&lt;div&gt; 而不是&lt;script&gt; 元素后,我的代码看起来像这样......在 IE 10 上运行良好。但是,后来 当我在 IE 上测试它时8,它抛出了错误:

You cannot apply bindings multiple times to the same element

HTML:

<div id="mainKnockoutDiv" class="measurementsDivContents hourlyMeasurements">

  <div id="saveButtons_template" style="display: none;">
    ... my template content here ...
  </div>

  <!--ko template:  name: 'saveButtons_template'  -->
  <!--/ko-->

  Some_HTML_content_here....

  <!--ko template:  name: 'saveButtons_template'  -->
  <!--/ko-->

</div>

javascript

ko.applyBindings(viewModel, document.getElementById('mainKnockoutDiv'));

解决方案

我所要做的就是将我的saveButtons_template&lt;div&gt; 移到底部,使其位于mainKnockoutDiv 之外。这解决了我的问题。

我想 Knockout.js 试图多次绑定我的模板 &lt;div&gt;,因为它位于 applyBindings 目标区域内......并且没有使用 &lt;script&gt; 元素......并且被引用作为模板。

【讨论】:

是的。将我的脚本包含从目标 HTML 之前移动到目标 HTML 之后对我来说是诀窍。【参考方案6】:

更新答案

现在我们可以使用dataFor() 来检查绑定是否已应用,我宁愿检查数据绑定,而不是cleanNode()applyBindings()

像这样:

var koNode = document.getElementById('formEdit');
var hasDataBinding = !!ko.dataFor(koNode);
console.log('has data binding', hasDataBinding);
if (!hasDataBinding)  ko.applyBindings(vm, koNode);

原始答案。

已经有很多答案了!

首先,假设我们需要在一个页面中进行多次绑定是相当普遍的。比如说,我在 Bootstrap 模式中有一个表单,它将一次又一次地加载。许多表单输入都有双向绑定。

我通常采取简单的方法:每次绑定前清除绑定。

var koNode = document.getElementById('formEdit');
ko.cleanNode(koNode);
ko.applyBindings(vm, koNode);

只需确保此处koNode 是必需的,因为ko.cleanNode() 需要一个节点元素,即使我们可以在ko.applyBinding(vm) 中省略它。

【讨论】:

看起来不推荐使用 ko.cleanNode(),因为它是由 Knockout 内部使用的,而不是它的文档。 ***.com/questions/15063794/…。所以“dataFor”看起来使用起来更安全。 knockoutjs.com/documentation/unobtrusive-event-handling.html【参考方案7】:

如果您一遍又一遍地重用一个元素(在我的例子中是一个 Bootstrap 模式对话框),那么多次调用 ko.applyBindings(el) 会导致这个问题。

而是像这样只做一次:

if (!applied) 
    ko.applyBindings(el);
    applied = true;

或者像这样:

var apply = function (viewModel, containerElement) 
    ko.applyBindings(viewModel, containerElement);
    apply = function() ; // only allow this function to be called once.

PS:如果您使用映射插件并将您的 JSON 数据转换为 observables,这可能会更频繁地发生在您身上。

【讨论】:

【参考方案8】:

我终于通过在绑定处理程序的init 函数中返回 controlsDescendantBindings: true 解决了我的问题。见this。

【讨论】:

【参考方案9】:

这个问题有很多很好的答案,但我有一个新手答案。

我发现我不小心在两个地方添加了相同的脚本,它试图绑定两次。所以在你拔头发之前,一定要检查一下这个问题。

【讨论】:

【参考方案10】:

可能发生的引发此异常的情况如下。假设你有:

ko.applyBindings(myViewModel1, document.getElementById('element1'));
...
ko.applyBindings(myViewModel2, document.getElementById('element2'));

现在,当 #element1#element2 都不存在时,您将收到错误消息。原因是当找不到#element1#element2 时,Knockout 的applyBindings 会退回到document.body 作为根元素。现在它尝试在 body 上应用两次绑定...

如果你问我,这不是 Knockout 的一个很好的后备方案。我宁愿有一个明确的错误消息,表明该元素在 DOM 中不存在(尚)。

【讨论】:

解决了我的问题。我在做document.getElementById('#element1')。忘记了 # 是 jquery 的东西。如果敲除会引发“DOM 中不存在元素”错误,那确实会好得多。 我有竞态条件?行为不同的 Chrome/FF/IE。我的解决方案是将它包装在一个 try-catch 中,它消除了症状并使页面正常工作。阅读此答案后,我还检查了该控件是否在 applyBindings 之前存在,并且可以看到浏览器之间的差异。 这是我的问题,我试图运行 Jasmine 单元测试并想知道为什么会出现这个错误(视图模型所附加的元素未包含在测试 html 中)【参考方案11】:

您需要在再次使用applyBindings 之前删除绑定:

ko.cleanNode($element[0]);

【讨论】:

我在做什么:我创建了一个自定义绑定,并在里面动态地将一个变量绑定到一个元素。见:***.com/a/9370953/809536 新问题:***.com/questions/18883556/… 这对我有用var _data = $("view-data"); ko.cleanNode(_data); ko.cleanNode() 似乎不是避免“多次应用绑定”错误的推荐方法。 ***.com/a/15069509/538962 无法让它与 $('view-data') 或 $('#knockoutDiv')[0] 一起工作。它不断给出 applybindings 错误。我在应用绑定之前添加了 cleanNode 调用。【参考方案12】:

您不应该对视图多次应用绑定。在 2.2 中,该行为未定义,但仍不受支持。在 2.3 中,它现在正确显示错误。使用淘汰赛时,目标是对页面上的视图应用一次绑定,然后使用对视图模型上的可观察对象的更改来更改页面上视图的外观和行为。

【讨论】:

editAssignedPartial 是在与页面加载不同的事件中动态添加到页面的。如果我从点击事件中删除 applyBidndings,它不会显示。有没有更好的方法来做动态加法? 有时将 VM 多次绑定到视图是有意义的。例如,在带有导航栏和 div 以显示每个可点击导航栏元素的内容的 UI 中,您可以为 X 个导航栏元素设置 X 数量的 div,或者只使用一个 div 并将 VM 绑定到该 div元素被点击。 我遇到了同样的问题。我有需要重新绑定的动态内容。 另一种方法是使用模板。您可以在页面中动态添加模板&lt;script type='text/html'&gt;...&lt;/script&gt;,以控制动态内容。 我喜欢使用模板而不是 cleanNode 方法的想法。谢谢你的建议。【参考方案13】:

要使上述解决方案发挥作用,有两点很重要:

    应用绑定时,需要指定作用域(元素)!!

    清除绑定时,您必须指定与作用域完全相同的元素。

代码如下

标记

<div id="elt1" data-bind="with: data">
    <input type="text" data-bind="value: text1" >
</form>

绑定视图

var myViewModel = 
  "data" : 
    "text1" : "bla bla"
  
:

Javascript

ko.applyBindings(myViewModel, document.getElementById('elt1'));

清除绑定

ko.cleanNode(document.getElementById('elt1'));

【讨论】:

我在 DevExtreme 移动应用中使用了这个解决方案。由于 DevX 会自动运行 ko.applyBindings,因此我必须先 ko.cleanNode,然后再运行 ko.applyBindings。谢谢米贾! 为什么在cleanNode()中使用jQuery选择器? 是一个jquery选择器,但是记法[0]获取的是jquery对象的DOM对象。我同意这不是必需的,最好使用纯 DOM 对象或 jQuery 对象。 @MitjaGustin 为什么人们不建议使用 ko.cleanNode? (感谢这对我有用)

以上是关于Knockout.js v2.3.0 错误“您不能将绑定多次应用于同一元素”的主要内容,如果未能解决你的问题,请参考以下文章

我是使用 Knockout js 的新手,我尝试显示一个列表,但出现以下错误

多视图模型破坏了 knockout.js

KnockOut.js 验证始终为真/errors.length = 0

Knockout JS foreach 作为函数的输入

Knockout.js如何与组件共享viewmodel observable以进行双向绑定

使用 Spring MVC 和 Knockout JS 进行服务器端验证