2012-04-06 101 views
104

我正在將功能構建到用戶可以多次執行的網頁上。通過用戶的操作,使用ko.applyBindings()創建對象/模型並將其應用於HTML。如何清除/刪除Knockout.js中的可觀察綁定?

數據綁定HTML通過jQuery模板創建。

到目前爲止這麼好。

當我重複此步驟通過創建一個第二對象/模型並調用ko.applyBindings()我遇到兩個問題:

  1. 的標記示出了以前的對象/模型以及新對象/模型。
  2. 與對象/模型中的其中一個屬性發生javascript錯誤,雖然它仍然呈現在標記中。

爲了解決這個問題,在第一次傳遞之後,我調用jQuery的.empty()來移除包含所有數據綁定屬性的模板HTML,以便它不再位於DOM中。當用戶啓動第二遍的進程時,數據綁定的HTML被重新添加到DOM。但是正如我所說的,當HTML被重新添加到DOM並重新綁定到新的對象/模型時,它仍然包含來自第一個對象/模型的數據,並且我仍然得到JS錯誤在第一階段不會發生。

結論似乎是Knockout堅持這些綁定的屬性,即使標記從DOM中刪除。

所以我在找的是從Knockout中移除這些綁定屬性的方法;告訴淘汰賽,不再有可觀察的模型。有沒有辦法做到這一點?

EDIT

的基本過程是,用戶上傳的文件;服務器然後用JSON對象響應,則數據綁定HTML被添加到DOM,則JSON對象模型被綁定到使用

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

這個HTML一旦用戶已經在模型上進行了一些選擇,所述同一個對象被回傳到服務器,數據綁定HTML從那麼DOM取出,然後我有以下JS

mn.AccountCreationModel = null; 

當用戶希望這樣做一次,重複所有這些步驟。

恐怕代碼太'參與'做一個jsFiddle演示。

+0

調用ko。不建議多次應用綁定,特別是在包含相同的dom元素的情況下。可能有另一種方法來實現你想要的。你將需要提供更多的代碼。請儘可能包含jsfiddle。 – madcapnmckay 2012-04-06 21:10:39

+0

爲什麼不公開一個'init'函數來傳遞要應用的數據? – KyorCode 2014-01-29 09:59:13

回答

156

您是否嘗試過在您的DOM元素上調用knockout的clean節點方法來處理內存綁定對象?

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

然後在新的視圖模型上再次對該元素應用敲除綁定將更新您的視圖綁定。

+32

這工作 - 謝謝。不過,我無法找到有關此方法的任何文檔。 – awj 2012-04-07 07:43:44

+0

我也搜索了關於這個挖空效用函數的文檔。就像我從源代碼中可以看到的那樣,它正在調用dom元素本身的某些鍵上的「delete」,這顯然是存儲所有淘汰賽魔術的地方。如果任何人有文件來源,我會非常感激。 – 2012-11-09 15:40:59

+2

你不會找到它。我已經在ko實用程序功能上搜索了高和低的文檔,但都不存在。這篇博客文章是您找到的最接近的文章,但它僅涵蓋ko.utils的成員:http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html – 2012-12-28 17:14:13

28

對於我正在處理的項目,我編寫了一個簡單的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]); 
    } 
}; 
+0

只需要注意一點,我還沒有測試過重新綁定到剛剛調用了'ko.cleanNode()'的東西,而不是整個html被替換。 – 2012-11-21 03:57:54

+4

不會解除所有其他事件綁定嗎?有沒有可能只刪除ko的事件處理程序? – lordvlad 2013-02-13 16:54:34

+0

而不改變ko核心,這是 – lordvlad 2013-02-13 17:16:50

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> 
+1

我花了至少4個小時試圖解決類似的問題,只有阿米爾的解決方案適合我。 – 2013-04-17 11:39:34

+0

@AntoninJelinek我在我的場景中經歷過的其他事情,徹底刪除html,並動態地追加它絕對刪除一切。 比如我有

上$就成功的結果,我每次 調用 $服務器方法(「#knockoutContainerDiv」)下面做淘汰賽的代碼div容器children.remove(); //刪除它 調用的方法內容來追加動態HTML與淘汰賽代碼 $(「#knockoutContainerDiv」)。追加(「與淘汰賽綁定代碼childelements」) 並調用applyBinding再次 – 2013-04-18 03:33:42

+1

嘿ammir,我有相當多的和你一樣的場景。除了我必須使用ko.cleanNode(element)這個事實外,你提到的所有東西都很好用;之前每次重新綁定。 – 2014-07-29 21:26:04

1

我發現,如果視圖模型包含了很多的div綁定清除ko.applyBindings(new someModelView);最好的方法是使用:ko.cleanNode($("body")[0]);這允許你動態調用一個新的ko.applyBindings(new someModelView2);沒有以前的視圖模型的擔心仍然綁定。

+3

我想補充幾點:(1)這將清除網頁上的所有綁定,這可能適合您的應用程序,但我想有很多應用程序綁定已添加到頁面的多個部分單獨的原因。對於許多用戶來說,在單個掃描命令中清除所有綁定可能沒有幫助。 (2)檢索'$(「body」)[0]'的一個更快,更高效的原生JavaScript方法是'document.body'。 – awj 2013-07-04 08:15:19

12

你可以嘗試使用的是與結合淘汰賽提供: 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);

淘汰賽會照顧HTML的 - 即當模型不爲空和div的內容就會出現(與「具有約束力」的那個)將消失。

4

我認爲保持綁定整個過程可能會更好,只需更新與其關聯的數據即可。我遇到了這個問題,並發現只使用在我保存數據的陣列上調用.resetAll()方法是最有效的方法。

基本上你可以用它包含數據通過視圖模型被渲染一些全局變量開始:

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

一旦你做到了這一點,你可以繼續使用應用綁定您的視圖模型和往常一樣:

function MyViewModel() { 
    var self = this; 
    self.myData = myLiveData; 
}; 
ko.applyBindings(new MyViewModel()); 

然後在HTML只使用myData作爲你通常會。

這樣,你可以用myLiveData從任何函數中去掉。例如,如果您想每隔幾秒更新一次,只需在功能中包裝$.getJSON行並在其上調用setInterval即可。只要您記得保留myLiveData.removeAll();行,就永遠不需要刪除綁定。

除非您的數據真的很大,否則用戶甚至無法注意到重置數組之間的時間,然後重新添加最新數據。

+0

自發布問題以來,這就是我現在所做的。只是這些Knockout方法中的一些或者沒有記錄,或者你真的需要四處看看(已經知道函數名)以找到它的功能。 – awj 2013-10-22 13:36:24

+0

我不得不非常努力地找到這個(當通過文檔挖掘的小時數超過所得到的代碼行數...哇)。真高興你做到了。 – Zac 2013-10-22 17:11:56

5

除了使用KO的內部函數和處理JQuery的全局事件處理程序刪除外,更好的辦法是使用withtemplate綁定。當你這樣做時,ko重​​新創建DOM的那部分,並自動清理。這也是推薦的方式,請看這裏:https://stackoverflow.com/a/15069509/207661

1

你有沒有想過這一點:

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

所以對我來說它不是一個真正的問題,它已經綁定了,其錯誤沒有被發現並處理...