如何清除/删除Knockout.js中的可观察绑定?


113

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

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

到目前为止,一切都很好。

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

  1. 标记显示先前的对象/模型以及新的对象/模型。
  2. 尽管对象/模型中的属性之一仍在标记中呈现,但仍会发生JavaScript错误。

为了解决这个问题,在第一遍之后,我调用jQuery的.empty()来删除包含所有数据绑定属性的模板化HTML,以使其不再存在于DOM中。当用户开始第二遍的处理时,将数据绑定的HTML重新添加到DOM。

但是就像我说的那样,当HTML重新添加到DOM并重新绑定到新的对象/模型时,它仍然包含来自第一个对象/模型的数据,并且我仍然遇到未发生的JS错误。在第一遍。

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

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

编辑

基本过程是用户上传文件;然后服务器使用JSON对象进行响应,将数据绑定的HTML添加到DOM,然后使用以下方法将该JSON对象模型绑定到该HTML

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

用户在模型上进行了一些选择后,将同一对象发布回服务器,从DOM中删除了数据绑定的HTML,然后得到了以下JS

mn.AccountCreationModel = null;

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

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


不建议多次调用ko.applyBindings,尤其是在包含dom的同一元素上。可能有另一种方式来实现您想要的。您将需要提供更多代码。如果可能,请包括一个jsfiddle。
madcapnmckay 2012年

为什么不公开init传递要应用的数据的功能?
KyorCode 2014年

Answers:


169

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

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

然后,使用新的视图模型在该元素上再次应用敲除绑定将更新您的视图绑定。


33
这有效-谢谢。但是,我找不到有关此方法的任何文档。
2012年

我也已经搜索了有关此敲除实用程序功能的文档。从源代码可以看出,它正在调用deletedom元素本身上的某些键,这显然是存储所有敲除魔术的位置。如果有人提供文档资料,我将非常有义务。
Patrick M

2
你找不到。我在ko实用程序功能的文档中搜索过高和低,但是都不存在。这篇博客文章是您找到的最近的文章,但仅涵盖ko.utils的成员:kickmeout.net/2011/04/utility-functions-in-knockoutjs.html
Nick Daniels

1
您还希望手动删除事件,如下面我的答案所示。
Michael Berkompas

1
@KodeKreachor我已经在下面发布了工作示例。我的观点是,最好保持绑定完整,同时在ViewModel中释放数据。这样,您就不必处理取消绑定/重新绑定的问题。看起来比使用未公开文档的方法直接从DOM手动取消绑定更干净。除此之外,CleanNode还是有问题的,因为它不释放任何事件处理程序(有关更多详细信息,请参见此处的答案:stackoverflow.com/questions/15063794/…
Zac 2013年

31

对于我正在从事的项目,我编写了一个简单的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。
Michael Berkompas 2012年

4
您的解决方案也不会取消绑定所有其他事件绑定吗?有可能只删除ko的事件处理程序吗?
lordvlad

而不更改ko核心
lordvlad

1
没错,但就我所知,如果没有核心改动,这是不可能的。看到我在这里提出的这个问题:github.com/SteveSanderson/knockout/issues/724
Michael Berkompas 2013年

难道不是您很少应该自己触摸dom的想法吗?这个答案遍历了dom,并且在我的用例中肯定是远远不能使用的。
Blowsie

12

您可以尝试使用基因剔除提供的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);

淘汰赛将处理html-即,当模型不为空时它将出现,并且div的内容(带有“ with binding”的内容)将消失。


9

每次单击搜索按钮时,我必须调用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>

1
我已经花费了至少4个小时来尝试解决类似的问题,只有aamir的解决方案对我有效。
Antonin Jelinek 2013年

@AntoninJelinek我在场景中遇到的另一件事是完全删除html,然后再次动态添加它以绝对删除所有内容。例如,我在$ .Ajax上有敲除代码容器div <div id =“ knockoutContainerDiv”> </ div>。每次服务器方法调用$(“#knockoutContainerDiv”)。children.remove(); /之后,我都会执行成功结果/删除它的内容,调用该方法以添加带有剔除代码$(“#knockoutContainerDiv”)。append(“带有剔除绑定代码的子元素”)的动态html并再次调用applyBinding
aamir sajjad 2013年

1
嗨,阿米尔,我的情况和您差不多。除了我必须使用ko.cleanNode(element);之外,您提到的所有内容都运行良好。每次重新绑定之前。
拉多斯拉夫·明切夫

@RadoslavMinchev您认为我可以为您提供进一步的帮助吗?
2014年

谢谢。@aamirsajjad,只是想提到对我有用的是调用cleanNode()函数使其起作用。
拉多斯拉夫·明切夫

6

与其使用KO的内部功能而不是处理JQuery的毯式事件处理程序,不如使用withtemplate绑定。当您执行此操作时,ko将重新创建DOM的该部分,因此将自动清除它。这也是推荐的方式,请参见:https//stackoverflow.com/a/15069509/207661


4

我认为最好始终保持绑定状态,并仅更新与其关联的数据。我遇到了这个问题,发现仅使用.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方法没有文档记录,或者您真的需要四处浏览(已经知道函数名)以查找其作用。
2013年

我也很难看起来也很难找到它(当通过文档挖掘的小时数超过了结果的代码行数时……哇)。真高兴你做到了。
Zac


1

您是否考虑过:

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

我想到了这个,因为在淘汰赛中,我找到了这段代码

    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);
    }

所以对我来说,这并不是一个已经被束缚的问题,没有发现并处理错误。


0

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


4
我要补充两点:(1)这将清除您网页上的所有绑定,这可能适合您的应用程序,但是我想有很多应用程序已将绑定添加到页面的多个部分中以进行单独的处理。原因。对于许多用户而言,用单个清除命令清除所有绑定可能没有帮助。(2)一种更快,更有效的原生JavaScript检索$("body")[0]方法document.body
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.