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

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

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

目前为止还不错。

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

  1. The markup shows the previous object/model as well as the new object/model.
  2. 一个 javascript 错误与对象/模型中的一个属性有关,尽管它仍然在标记中呈现。

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

But like I said, when the HTML is re-added to the DOM and re-bound to the new object/model, it still includes data from the the first object/model, and I still get the JS error which doesn't occur during the first pass.

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

所以我要寻找的是一种方法,去除敲除这些绑定属性,告诉敲除,不再有一个可观察的模型。有办法吗?

剪辑

The basic process is that the user uploads a file; the server then responds with a JSON object, the data-bound HTML is added to the DOM, then the JSON object model is bound to this HTML using

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

Once the user has made some selections on the model, the same object is posted back to the server, the data-bound HTML is removed from then DOM, and I then have the following JS

mn.AccountCreationModel = null;

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

恐怕代码太复杂了,不能做 jsFiddle 演示。

116652 次浏览

您是否尝试过在 DOM 元素上调用 Knokout 的 clean node 方法来释放内存绑定对象?

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

然后将淘汰绑定再次应用到带有新视图模型的元素上,这样就可以更新视图绑定。

对于我正在做的一个项目,我编写了一个简单的 ko.unapplyBindings函数,它接受一个 jQuery 节点和删除布尔值。它首先解除所有 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]);
}
};

I have to call ko.applyBinding each time search button click, and filtered data is return from server, and in this case following work for me without using ko.cleanNode.

我的经验是,如果我们用模板替换 foreach,那么在集合/Observer ableArray 的情况下应该可以正常工作。

You may find this scenario useful.

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


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

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

您可以尝试使用淘汰提供的 with 绑定: Http://knockoutjs.com/documentation/with-binding.html 其思想是使用一次应用绑定,当数据发生更改时,只需更新模型。

Lets say you have a top level view model storeViewModel, your cart represented by cartViewModel, 以及该购物车中的项目列表——比如 cartItemsViewModel。

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

Lets assume that the cartItemsViewModel has the following structure:

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

CartItemsViewModel 在开始时可以为空。

The steps would look like this:

  1. Define bindings in html. Separate the cartItemsViewModel binding.

    
    <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. The store model comes from your server (or is created in any other way).

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. Define empty models on your top level view model. Then a structure of that model can be updated with actual data.

    
    storeViewModel.cartItemsViewModel = ko.observable();
    storeViewModel.cartViewModel = ko.observable();
    
    
  4. Bind the top level view model.

    ko.applyBindings(storeViewModel);

  5. When the cartItemsViewModel object is available then assign it to the previously defined placeholder.

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

If you would like to clear the cart items: storeViewModel.cartItemsViewModel(null);

Knockout will take care of html - i.e. it will appear when model is not empty and the contents of div (the one with the "with binding") will disappear.

我认为最好是一直保持绑定,并简单地更新与之相关的数据。我遇到了这个问题,并且发现对保存数据的数组使用 .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());

Then in the HTML just use myData as you normally would.

This way, you can just muck with myLiveData from whichever function. For instance, if you want to update every few seconds, just wrap that $.getJSON line in a function and call setInterval on it. You'll never need to remove the binding as long as you remember to keep the myLiveData.removeAll(); line in.

除非您的数据非常庞大,否则用户甚至不会注意到在重置数组和添加最新数据之间的时间间隔。

与其使用 KO 的内部函数和处理 JQuery 的整体事件处理程序删除,不如使用 withtemplate绑定。执行此操作时,ko 将重新创建 DOM 的该部分,因此它将自动被清理。这也是推荐的方法,看这里: https://stackoverflow.com/a/15069509/207661

我有一个内存泄漏问题最近和 ko.cleanNode(element);不会为我这样做-ko.removeNode(element);

你有没有想过:

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

所以对我来说,这不是一个问题,它已经约束,它的错误没有捕捉和处理..。

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