【问题标题】:How to clear/remove observable bindings in Knockout.js?如何清除/删除 Knockout.js 中的可观察绑定?
【发布时间】:2012-04-20 08:48:01
【问题描述】:

我正在将功能构建到用户可以多次执行的网页上。通过用户的操作,一个对象/模型被创建并使用 ko.applyBindings() 应用到 HTML。

数据绑定的 HTML 是通过 jQuery 模板创建的。

到目前为止一切顺利。

当我通过创建第二个对象/模型并调用 ko.applyBindings() 重复此步骤时,我遇到了两个问题:

  1. 标记显示以前的对象/模型以及新的对象/模型。
  2. 发生与对象/模型中的一个属性相关的 javascript 错误,尽管它仍呈现在标记中。

为了解决这个问题,在第一遍之后,我调用 jQuery 的 .empty() 来删除包含所有数据绑定属性的模板化 HTML,这样它就不再在 DOM 中了。当用户启动第二次传递的过程时,数据绑定的 HTML 将重新添加到 DOM。

但是就像我说的,当 HTML 重新添加到 DOM 并重新绑定到新的对象/模型时,它仍然包含来自第一个对象/模型的数据,我仍然得到 JS 错误'在第一次传递时不会发生。

结论似乎是 Knockout 保留了这些绑定属性,即使标记已从 DOM 中删除。

所以我正在寻找一种从 Knockout 中删除这些绑定属性的方法;告诉淘汰赛不再有可观察的模型。有没有办法做到这一点?

编辑

基本流程是用户上传文件;然后服务器响应一个 JSON 对象,数据绑定的 HTML 被添加到 DOM,然后 JSON 对象模型被绑定到这个 HTML 使用

mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);

一旦用户在模型上做了一些选择,同一个对象就会被发送回服务器,数据绑定的 HTML 会从 DOM 中删除,然后我就有了下面的 JS

mn.AccountCreationModel = null;

当用户希望再次执行此操作时,重复所有这些步骤。

恐怕代码太“涉及”,无法进行 jsFiddle 演示。

【问题讨论】:

  • 不建议多次调用 ko.applyBindings,尤其是在同一个包含的 dom 元素上。可能还有另一种方法可以实现您想要的。不过,您将需要提供更多代码。如果可能,请包含一个 jsfiddle。
  • 为什么不公开一个init 函数来传递要应用的数据?

标签: javascript knockout.js


【解决方案1】:

您是否尝试过在 DOM 元素上调用 knockout 的 clean node 方法来处理内存中绑定的对象?

var element = $('#elementId')[0]; 
ko.cleanNode(element);

然后使用新视图模型再次对那个元素应用剔除绑定将更新您的视图绑定。

【讨论】:

  • 这行得通 - 谢谢。不过,我找不到有关此方法的任何文档。
  • 我也搜索过有关此淘汰实用程序功能的文档。从源代码中我可以看出,它在 dom 元素本身的某些键上调用 delete,这显然是存储所有淘汰魔法的地方。如果有人有文档来源,我将不胜感激。
  • 你不会找到它。我已经搜索了关于 ko 实用程序函数的文档,但不存在。这篇博文是你能找到的最接近的东西,但它只涵盖了 ko.utils 的成员:knockmeout.net/2011/04/utility-functions-in-knockoutjs.html
  • 您还需要手动删除事件,如下面的回答所示。
  • @KodeKreachor 我已经在下面发布了工作示例。我的观点是,保持绑定完整,同时在 ViewModel 中释放数据可能会更好。这样你就不必处理取消/重新绑定。这似乎比使用未记录的方法直接从 DOM 手动解除绑定更干净。除此之外,CleanNode 是有问题的,因为它不会释放任何事件处理程序(有关更多详细信息,请参见此处的答案:stackoverflow.com/questions/15063794/…
【解决方案2】:

对于我正在进行的一个项目,我编写了一个简单的ko.unapplyBindings 函数,它接受一个jQuery 节点和remove 布尔值。它首先取消绑定所有 jQuery 事件,因为 ko.cleanNode 方法并没有解决这个问题。我已经测试了内存泄漏,它似乎工作得很好。

ko.unapplyBindings = function ($node, remove) {
    // unbind events
    $node.find("*").each(function () {
        $(this).unbind();
    });

    // Remove KO subscriptions and references
    if (remove) {
        ko.removeNode($node[0]);
    } else {
        ko.cleanNode($node[0]);
    }
};

【讨论】:

  • 只有一个警告,我没有测试重新绑定到刚刚调用 ko.cleanNode() 而不是整个 html 被替换的东西。
  • 您的解决方案不会同时解除所有其他事件绑定吗?是否有可能只删除 ko 的事件处理程序?
  • 不改变 ko 核心是
  • 没错,但据我所知,如果没有核心改动,这是不可能的。看到我在这里提出的这个问题:github.com/SteveSanderson/knockout/issues/724
  • KO 的想法不就是你自己应该很少接触 dom 吗?这个答案通过 dom 循环,并且在我的用例中肯定不会不可用。
【解决方案3】:

每次单击搜索按钮时,我都必须调用 ko.applyBinding,过滤后的数据会从服务器返回,在这种情况下,我无需使用 ko.cleanNode 即可完成以下工作。

我体验过,如果我们将 foreach 替换为模板,那么在 collections/observableArray 的情况下它应该可以正常工作。

你可能会发现这个场景很有用。

<ul data-bind="template: { name: 'template', foreach: Events }"></ul>

<script id="template" type="text/html">
    <li><span data-bind="text: Name"></span></li>
</script>

【讨论】:

  • 我至少花了 4 个小时试图解决类似的问题,只有 aamir 的解决方案对我有用。
  • @AntoninJelinek 我在我的场景中经历的另一件事是完全删除 html,并再次动态附加它以绝对删除所有内容。例如,我在每次服务器方法调用 $("#knockoutContainerDiv").children.remove();/ /remove 它的内容调用方法来附加动态 html 与淘汰代码 $("#knockoutContainerDiv").append("childelements with knockout binding code") 并再次调用 applyBinding
  • 嘿,阿米尔,我的情况和你差不多。你提到的一切都很好,除了我必须使用 ko.cleanNode(element);在每次重新绑定之前。
  • @RadoslavMinchev 您认为我可以进一步帮助您吗,如果可以,那么如何,我很乐意分享我对特定问题的想法/经验。
  • 谢谢。 @aamirsajjad,只是想提一下,对我有用的是调用 cleanNode() 函数使其工作。
【解决方案4】:

我发现如果视图模型包含许多 div 绑定,清除 ko.applyBindings(new someModelView); 的最佳方法是使用:ko.cleanNode($("body")[0]); 这允许您动态调用新的 ko.applyBindings(new someModelView2); 而无需担心之前的视图模型仍然被绑定。

【讨论】:

  • 我想补充几点:(1)这将清除您网页中的所有绑定,这可能适合您的应用程序,但我想有很多应用程序已经添加了绑定出于不同的原因访问页面的多个部分。对于许多用户来说,在一个单一的、全面的命令中清除所有绑定可能没有帮助。 (2) 一种更快、更高效的原生 JavaScript 检索 $("body")[0] 的方法是 document.body
【解决方案5】:

您可以尝试使用淘汰赛提供的 with 绑定: http://knockoutjs.com/documentation/with-binding.html 这个想法是使用一次应用绑定,每当您的数据发生变化时,只需更新您的模型。

假设您有一个顶级视图模型 storeViewModel,您的购物车由 cartViewModel 表示, 以及该购物车中的商品列表 - 比如说 cartItemsViewModel。

您可以将顶级模型 - storeViewModel 绑定到整个页面。然后,您可以将页面中负责购物车或购物车项目的部分分开。

假设 cartItemsViewModel 具有以下结构:

var actualCartItemsModel = { CartItems: [
  { ItemName: "FirstItem", Price: 12 }, 
  { ItemName: "SecondItem", Price: 10 }
] }

cartItemsViewModel 开头可以为空。

步骤如下所示:

  1. 在 html 中定义绑定。分离 cartItemsViewModel 绑定。

    <div data-bind="with: cartItemsViewModel"> <div data-bind="foreach: CartItems"> <span data-bind="text: ItemName"></span> <span data-bind="text: Price"></span> </div> </div>
  2. 商店模型来自您的服务器(或以任何其他方式创建)。

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. 在顶层视图模型上定义空模型。然后可以更新该模型的结构 实际数据。

    storeViewModel.cartItemsViewModel = ko.observable(); storeViewModel.cartViewModel = ko.observable();
  4. 绑定顶层视图模型。

    ko.applyBindings(storeViewModel);

  5. 当 cartItemsViewModel 对象可用时,将其分配给之前定义的占位符。

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

如果您想清除购物车物品: storeViewModel.cartItemsViewModel(null);

Knockout 会处理 html - 即它会在模型​​不为空时出现,并且 div 的内容(带有“绑定”的那个)将消失。

【讨论】:

    【解决方案6】:

    我认为最好始终保持绑定,并简单地更新与之关联的数据。我遇到了这个问题,发现只在我保存数据的数组上调用.resetAll() 方法是最有效的方法。

    基本上,您可以从一些包含要通过 ViewModel 呈现的数据的全局变量开始:

    var myLiveData = ko.observableArray();
    

    我花了一段时间才意识到我不能只使 myLiveData 成为一个普通数组——ko.oberservableArray 部分很重要。

    然后你可以继续做任何你想做的事情myLiveData。例如,拨打$.getJSON 电话:

    $.getJSON("http://foo.bar/data.json?callback=?", function(data) {
        myLiveData.removeAll();
        /* parse the JSON data however you want, get it into myLiveData, as below */
        myLiveData.push(data[0].foo);
        myLiveData.push(data[4].bar);
    });
    

    完成此操作后,您可以照常使用 ViewModel 应用绑定:

    function MyViewModel() {
        var self = this;
        self.myData = myLiveData;
    };
    ko.applyBindings(new MyViewModel());
    

    然后在 HTML 中像往常一样使用myData

    这样,您可以从任何函数中获取 myLiveData。例如,如果您想每隔几秒更新一次,只需将 $.getJSON 行包装在一个函数中并在其上调用 setInterval。只要您记得保留myLiveData.removeAll(); 行,就永远不需要删除绑定。

    除非你的数据真的很大,否则用户甚至不会注意到重置数组和添加最新数据之间的时间。

    【讨论】:

    • 自发布问题以来,这就是我现在所做的。只是其中一些 Knockout 方法要么没有记录,要么你真的需要环顾四周(已经知道函数名称)才能找到它的作用。
    • 我也不得不非常努力地找到它(当挖掘文档的小时数超过了生成的代码行数时......哇)。很高兴你成功了。
    【解决方案7】:

    使用withtemplate 绑定,而不是使用KO 的内部函数和处理JQuery 的一揽子事件处理程序删除,更好的主意。当您这样做时,ko 会重新创建 DOM 的该部分,因此它会自动被清理。这也是推荐的方式,看这里:https://stackoverflow.com/a/15069509/207661

    【讨论】:

      【解决方案8】:

      我最近遇到了内存泄漏问题,ko.cleanNode(element); 不会为我解决 -ko.removeNode(element); 会。 Javascript + Knockout.js memory leak - How to make sure object is being destroyed?

      【讨论】:

      • 在淘汰赛 3.1 中,ko.removeNode 实际上调用了 ko.cleanNode。不过,我不知道早期版本就是这种情况。
      【解决方案9】:

      你有没有想过这个:

      try {
          ko.applyBindings(PersonListViewModel);
      }
      catch (err) {
          console.log(err.message);
      }
      

      我想出这个是因为在 Knockout 中,我找到了这段代码

          var alreadyBound = ko.utils.domData.get(node, boundElementDomDataKey);
          if (!sourceBindings) {
              if (alreadyBound) {
                  throw Error("You cannot apply bindings multiple times to the same element.");
              }
              ko.utils.domData.set(node, boundElementDomDataKey, true);
          }
      

      所以对我来说,它已经绑定并不是一个真正的问题,而是错误没有被捕获和处理......

      【讨论】:

        【解决方案10】:
                    <div id="books">
                        <ul data-bind="foreach: booksImReading">
                            <li data-bind="text: name"></li>
                        </ul>
                    </div>
                    
                    var bookModel = {
                        booksImReading: [
                            { name: "Effective Akka" }, 
                            { name: "Node.js the Right Way" }]
                    };
                                                
                    ko.applyBindings(bookModel, el);
                    
                    var bookModel2 = {
                        booksImReading: [
                            { name: "SQL Performance Explained" },
                            { name: "Code Connected" }]
                    };
                    
                    ko.cleanNode(books);
                    ko.applyBindings(bookModel2, books);
        

        【讨论】:

        • 嗨达拉!即使您的代码看起来很简单并且是一个很好的答案,始终建议您添加一些您自己的解释,以帮助其他人理解您追求的想法。谢谢!
        猜你喜欢
        • 1970-01-01
        • 2015-01-18
        • 1970-01-01
        • 1970-01-01
        • 2015-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多