首页 文章

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

提问于
浏览
111

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

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

到现在为止还挺好 .

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

  • 标记显示上一个对象/模型以及新对象/模型 .

  • 与对象/模型中的某个属性相关的javascript错误,尽管它仍然在标记中呈现 .

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

但就像我说的那样,当HTML重新添加到DOM并重新绑定到新的对象/模型时,它仍然包含来自第一个对象/模型的数据,我仍然得到不会发生的JS错误在第一次通过期间 .

结论似乎是Knockout坚持这些绑定属性,即使从DOM中删除了标记 .

所以我正在寻找的是从Knockout中删除这些绑定属性的方法;告诉淘汰赛,不再有可观察的模型 . 有没有办法做到这一点?

EDIT

基本过程是用户上传文件;然后服务器使用JSON对象进行响应,将数据绑定的HTML添加到DOM中,然后将JSON对象模型绑定到此HTML

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

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

mn.AccountCreationModel = null;

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

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

9 回答

  • 6

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

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

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

  • 2

    对于我正在研究的项目,我写了一个简单的 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]);
        }
    };
    
  • 1

    你可以尝试使用knockout提供的with binding:http://knockoutjs.com/documentation/with-binding.html想法是使用apply bindings一次,每当你的数据发生变化时,只需更新你的模型 .

    假设您有一个顶级视图模型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”的那个)将消失 .

  • 12

    我必须在每次单击搜索按钮时调用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>
    
  • 0

    而不是使用KO 's internal functions and dealing with JQuery'的毯子事件处理程序删除,更好的主意是使用 withtemplate 绑定 . 当你这样做时,ko重新创建DOM的那一部分,因此它会自动清理 . 这也是推荐的方式,见这里:https://stackoverflow.com/a/15069509/207661 .

  • 30

    我认为最好在整个时间内保持绑定,并简单地更新与之关联的数据 . 我遇到了这个问题,发现只是在我保存数据的数组上使用 .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(); 行,就永远不需要删除绑定 .

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

  • 4

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

  • 164

    你有没有想过这个:

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

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

  • 9

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

相关问题