我正在网页上构建功能,用户可以多次执行此功能。通过用户的操作,使用ko.applyBindings()创建对象/模型并将其应用于HTML。
数据绑定HTML是通过jQuery模板创建的。
到目前为止一切顺利。
当我通过创建第二个对象/模型并调用ko.applyBindings()重复此步骤时,我遇到两个问题:
为了解决这个问题,在第一次传递之后,我调用jQuery的.empty()来删除包含所有数据绑定属性的模板化HTML,这样它就不再存在于DOM中了。当用户启动第二次传递的过程时,数据绑定的HTML将重新添加到DOM中。
但就像我说的那样,当HTML重新添加到DOM并重新绑定到新对象/模型时,它仍然包含来自第一个对象/模型的数据,并且我仍然得到JS错误在第一次通过期间发生。
结论似乎是Knockout正在坚持这些绑定属性,即使从DOM中删除了标记。
所以我正在寻找的是从Knockout中删除这些绑定属性的方法;告诉淘汰赛,不再有可观察的模型。有没有办法做到这一点?
修改
基本过程是用户上传文件;服务器然后用JSON对象响应,将数据绑定的HTML添加到DOM,然后使用
将JSON对象模型绑定到此HTMLmn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);
一旦用户对模型做了一些选择,就会将相同的对象发回服务器,从DOM中删除数据绑定的HTML,然后我有以下JS
mn.AccountCreationModel = null;
当用户希望再次执行此操作时,将重复所有这些步骤。
我担心代码太“参与”了jsFiddle演示。
答案 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在开头可以为空。
步骤如下:
在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);
如果您想清除购物车商品:
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)
使用with
或template
绑定更好的想法,而不是使用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);