115

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

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

到现在为止还挺好。

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

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

为了解决这个问题,在第一次通过后,我调用 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);

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

mn.AccountCreationModel = null;

当用户希望再次这样做时,重复所有这些步骤。

恐怕代码太“参与”了,无法进行 jsFiddle 演示。

4

10 回答 10

174

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

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

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

于 2012-04-06T22:00:20.820 回答
31

对于我正在处理的一个项目,我编写了一个简单的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]);
    }
};
于 2012-11-19T18:30:47.087 回答
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);

Knockout 将处理 html - 即它会在模型​​不为空时出现,并且 div 的内容(具有“绑定”的那个)将消失。

于 2013-07-23T07:45:59.930 回答
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>
于 2013-01-16T06:14:51.513 回答
6

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

于 2014-01-03T07:47:44.353 回答
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();线路,您就永远不需要删除绑定。

除非您的数据真的很大,否则用户甚至无法注意到重置数组和添加最新数据之间的时间。

于 2013-10-22T05:23:55.773 回答
2

我最近遇到了内存泄漏问题,并且ko.cleanNode(element);不会为我这样做 -ko.removeNode(element);确实如此。Javascript + Knockout.js 内存泄漏 - 如何确保对象被销毁?

于 2014-05-01T15:44:07.970 回答
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);
    }

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

于 2015-05-12T15:35:55.733 回答
0

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

于 2013-07-03T15:43:14.947 回答
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);
于 2021-06-23T12:07:53.267 回答