Javascript 如何清除/删除 Knockout.js 中的可观察绑定?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10048485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to clear/remove observable bindings in Knockout.js?
提问by awj
I'm building functionality onto a webpage which the user can perform multiple times. Through the user's action, an object/model is created and applied to HTML using ko.applyBindings().
我正在将功能构建到用户可以多次执行的网页上。通过用户的操作,创建一个对象/模型并使用 ko.applyBindings() 应用于 HTML。
The data-bound HTML is created through jQuery templates.
数据绑定 HTML 是通过 jQuery 模板创建的。
So far so good.
到现在为止还挺好。
When I repeat this step by creating a second object/model and call ko.applyBindings() I encounter two problems:
当我通过创建第二个对象/模型并调用 ko.applyBindings() 来重复此步骤时,我遇到了两个问题:
- The markup shows the previous object/model as well as the new object/model.
- A javascript error occurs relating to one of the properties in the object/model, although it's still rendered in the markup.
- 标记显示先前的对象/模型以及新的对象/模型。
- 发生与对象/模型中的一个属性相关的 javascript 错误,尽管它仍然在标记中呈现。
To get around this problem, after the first pass I call jQuery's .empty() to remove the templated HTML which contains all the data-bind attributes, so that it's no longer in the DOM. When the user starts the process for the second pass the data-bound HTML is re-added to the DOM.
为了解决这个问题,在第一遍之后,我调用 jQuery 的 .empty() 来删除包含所有数据绑定属性的模板化 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.
但是就像我说的,当 HTML 被重新添加到 DOM 并重新绑定到新的对象/模型时,它仍然包含来自第一个对象/模型的数据,并且我仍然收到不会发生的 JS 错误在第一关期间。
The conclusion appears to be that Knockout is holding on to these bound properties, even though the markup is removed from the DOM.
结论似乎是 Knockout 保留了这些绑定属性,即使标记已从 DOM 中删除。
So what I'm looking for is a means of removing these bound properties from Knockout; telling knockout that there is no longer an observable model. Is there a way to do this?
所以我正在寻找一种从 Knockout 中删除这些绑定属性的方法;告诉淘汰赛不再有可观察的模型。有没有办法做到这一点?
EDIT
编辑
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
基本流程是用户上传文件;然后服务器用一个 JSON 对象响应,数据绑定的 HTML 被添加到 DOM,然后 JSON 对象模型绑定到这个 HTML 使用
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
一旦用户对模型进行了一些选择,相同的对象就会被回发到服务器,然后从 DOM 中删除数据绑定的 HTML,然后我有以下 JS
mn.AccountCreationModel = null;
When the user wishes to do this once more, all these steps are repeated.
当用户希望再次这样做时,重复所有这些步骤。
I'm afraid the code is too 'involved' to do a jsFiddle demo.
恐怕代码太“涉及”了,无法进行 jsFiddle 演示。
回答by KodeKreachor
Have you tried calling knockout's clean node method on your DOM element to dispose of the in memory bound objects?
您是否尝试过在 DOM 元素上调用淘汰赛的干净节点方法来处理内存绑定对象?
var element = $('#elementId')[0];
ko.cleanNode(element);
Then applying the knockout bindings again on just that element with your new view models would update your view binding.
然后使用新的视图模型在该元素上再次应用敲除绑定将更新您的视图绑定。
回答by Michael Berkompas
For a project I'm working on, I wrote a simple ko.unapplyBindings
function that accepts a jQuery node and the remove boolean. It first unbinds all jQuery events as ko.cleanNode
method doesn't take care of that. I've tested for memory leaks, and it appears to work just fine.
对于我正在处理的一个项目,我编写了一个简单的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]);
}
};
回答by Sylwester Gryzio
You could try using the with binding that knockout offers: http://knockoutjs.com/documentation/with-binding.htmlThe idea is to use apply bindings once, and whenever your data changes, just update your model.
您可以尝试使用淘汰赛提供的 with 绑定:http: //knockoutjs.com/documentation/with-binding.html这个想法是使用一次应用绑定,每当您的数据发生变化时,只需更新您的模型。
Lets say you have a top level view model storeViewModel, your cart represented by cartViewModel, and a list of items in that cart - say cartItemsViewModel.
假设您有一个顶级视图模型 storeViewModel,您的购物车由 cartViewModel 表示,以及该购物车中的项目列表 - 比如说 cartItemsViewModel。
You would bind the top level model - the storeViewModel to the whole page. Then, you could separate the parts of your page that are responsible for cart or cart items.
您会将顶级模型 - storeViewModel 绑定到整个页面。然后,您可以将页面中负责购物车或购物车项目的部分分开。
Lets assume that the cartItemsViewModel has the following structure:
让我们假设 carItemsViewModel 具有以下结构:
var actualCartItemsModel = { CartItems: [
{ ItemName: "FirstItem", Price: 12 },
{ ItemName: "SecondItem", Price: 10 }
] }
The cartItemsViewModel can be empty at the beginning.
开始时,cartItemsViewModel 可以为空。
The steps would look like this:
步骤如下所示:
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>
The store model comes from your server (or is created in any other way).
var storeViewModel = ko.mapping.fromJS(modelFromServer)
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();
Bind the top level view model.
ko.applyBindings(storeViewModel);
When the cartItemsViewModel object is available then assign it to the previously defined placeholder.
storeViewModel.cartItemsViewModel(actualCartItemsModel);
在 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>
商店模型来自您的服务器(或以任何其他方式创建)。
var storeViewModel = ko.mapping.fromJS(modelFromServer)
在顶级视图模型上定义空模型。然后可以使用实际数据更新该模型的结构。
storeViewModel.cartItemsViewModel = ko.observable(); storeViewModel.cartViewModel = ko.observable();
绑定顶层视图模型。
ko.applyBindings(storeViewModel);
当cartItemsViewModel 对象可用时,将其分配给之前定义的占位符。
storeViewModel.cartItemsViewModel(actualCartItemsModel);
If you would like to clear the cart items:
storeViewModel.cartItemsViewModel(null);
如果您想清除购物车项目:
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.
Knockout 将处理 html - 即当模型不为空时它会出现并且 div 的内容(带有“绑定”的那个)将消失。
回答by aamir sajjad
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.
每次单击搜索按钮时,我都必须调用 ko.applyBinding,并从服务器返回过滤后的数据,在这种情况下,我不使用 ko.cleanNode 进行以下工作。
I experienced, if we replace foreach with template then it should work fine in case of collections/observableArray.
我经历过,如果我们用模板替换 foreach 那么它应该可以在集合/observableArray 的情况下正常工作。
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>
回答by Shital Shah
Instead of using KO's internal functions and dealing with JQuery's blanket event handler removal, a much better idea is using with
or template
bindings. When you do this, ko re-creates that part of DOM and so it automatically gets cleaned. This is also recommended way, see here: https://stackoverflow.com/a/15069509/207661.
与其使用 KO 的内部函数和处理 JQuery 的全面事件处理程序删除,不如使用with
或template
绑定更好的主意。当你这样做时,ko 会重新创建 DOM 的那部分,因此它会自动被清理。这也是推荐的方式,请参见此处:https: //stackoverflow.com/a/15069509/207661。
回答by Zac
I think it might be better to keep the binding the entire time, and simply update the data associated with it. I ran into this issue, and found that just calling using the .resetAll()
method on the array in which I was keeping my data was the most effective way to do this.
我认为最好始终保持绑定,并简单地更新与之关联的数据。我遇到了这个问题,发现仅使用.resetAll()
保存数据的数组上的方法进行调用是最有效的方法。
Basically you can start with some global var which contains data to be rendered via the ViewModel:
基本上,您可以从一些包含要通过 ViewModel 呈现的数据的全局变量开始:
var myLiveData = ko.observableArray();
It took me a while to realize I couldn't just make myLiveData
a normal array -- the ko.oberservableArray
part was important.
我花了一段时间才意识到我不能只制作myLiveData
一个普通的数组——这ko.oberservableArray
部分很重要。
Then you can go ahead and do whatever you want to myLiveData
. For instance, make a $.getJSON
call:
然后你可以继续做你想做的任何事情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);
});
Once you've done this, you can go ahead and apply bindings using your ViewModel as usual:
完成此操作后,您可以继续像往常一样使用 ViewModel 应用绑定:
function MyViewModel() {
var self = this;
self.myData = myLiveData;
};
ko.applyBindings(new MyViewModel());
Then in the HTML just use myData
as you normally would.
然后在 HTML 中myData
像往常一样使用。
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.
这样,您就可以从任何函数中使用 myLiveData。例如,如果您想每隔几秒更新一次,只需将该$.getJSON
行包装在一个函数中并调用setInterval
它。只要您记得将myLiveData.removeAll();
线保持在里面,您就永远不需要移除绑定。
Unless your data is really huge, user's won't even be able to notice the time in between resetting the array and then adding the most-current data back in.
除非你的数据真的很大,否则用户甚至不会注意到重置数组和重新添加最新数据之间的时间。
回答by Matas Vaitkevicius
I had a memory leak problem recently and ko.cleanNode(element);
wouldn't do it for me -ko.removeNode(element);
did. Javascript + Knockout.js memory leak - How to make sure object is being destroyed?
我最近遇到了内存泄漏问题,ko.cleanNode(element);
并且不会为我解决 -ko.removeNode(element);
确实如此。Javascript + Knockout.js 内存泄漏 - 如何确保对象被销毁?
回答by ozzy432836
Have you thought about this:
你有没有想过这个:
try {
ko.applyBindings(PersonListViewModel);
}
catch (err) {
console.log(err.message);
}
I came up with this because in Knockout, i found this code
我想出这个是因为在淘汰赛中,我找到了这个代码
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);
}
So to me its not really an issue that its already bound, its that the error was not caught and dealt with...
所以对我来说,它并不是一个已经绑定的问题,它的错误没有被发现和处理......
回答by Derrick Hampton
I have found that if the view model contains many div bindings the best way to clear the ko.applyBindings(new someModelView);
is to use: ko.cleanNode($("body")[0]);
This allows you to call a new ko.applyBindings(new someModelView2);
dynamically without the worry of the previous view model still being binded.
我发现如果视图模型包含许多 div 绑定,最好的清除方法ko.applyBindings(new someModelView);
是使用:ko.cleanNode($("body")[0]);
这允许您ko.applyBindings(new someModelView2);
动态调用新的,而不必担心之前的视图模型仍然被绑定。