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

时间:2012-04-06 19:46:43

标签: javascript knockout.js

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

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

到目前为止一切顺利。

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

  1. 标记显示上一个对象/模型以及新对象/模型。
  2. 与对象/模型中的某个属性相关的javascript错误,虽然它仍然在标记中呈现。
  3. 为了解决这个问题,在第一次传递之后,我调用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);
    

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

    mn.AccountCreationModel = null;
    

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

    我担心代码太“参与”了jsFiddle演示。

10 个答案:

答案 0 :(得分:166)

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

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

然后使用新视图模型再次对该元素应用knockout绑定将更新视图绑定。

答案 1 :(得分:31)

对于我正在研究的项目,我写了一个简单的ko.unapplyBindings函数,它接受一个jQuery节点和remove boolean。它首先取消绑定所有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]);
    }
};

答案 2 :(得分:12)

您可以尝试使用knockout提供的with binding: 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);

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

    Knockout将处理html - 即当模型不为空时它会出现,div的内容(带有“with binding”的那个)将会消失。

答案 3 :(得分:9)

每次点击搜索按钮时都要调用ko.applyBinding,并且从服务器返回过滤后的数据,在这种情况下,不使用ko.cleanNode就可以为我工作。

我经历过,如果我们用模板替换foreach,那么在集合/ 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 :(得分:6)

使用withtemplate绑定更好的想法,而不是使用KO的内部函数和处理JQuery的一揽子事件处理程序。当你这样做时,ko重​​新创建DOM的那一部分,因此它会自动被清理。这也是推荐的方式,请参见此处:https://stackoverflow.com/a/15069509/207661

答案 5 :(得分:4)

我认为最好在整个时间内保持绑定,只需更新与之关联的数据即可。我遇到了这个问题,发现在我保存数据的数组上使用.resetAll()方法调用是最有效的方法。

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

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();行,就永远不需要删除绑定。

除非您的数据非常庞大,否则用户甚至无法注意重置阵列然后再添加最新数据的时间。

答案 6 :(得分:2)

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

答案 7 :(得分:1)

你有没有想过这个:

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

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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