2013-05-10 125 views
2

我的頁面中有一段代表視圖的標記,以及與該視圖關聯的JS控制器函數。 (這些是Angular,但我不相信這個問題。)控制器代碼監聽從應用程序中其他地方觸發的自定義事件,並使用某些特定於控制器的邏輯處理該事件。無法解除綁定jQuery自定義事件處理程序

我的問題是控制器的事件處理程序被連接了太多次:每次重新激活視圖時都會連接,導致處理程序在每次自定義事件觸發時都會運行多次。我只希望處理程序在每個事件中運行一次。

我已經嘗試使用.off()在綁定處理程序之前解除綁定處理程序;我試過.one()以確保處理程序只運行一次;並且在閱讀其與.off()here的相互作用之後我試過$.proxy()

這裏是我的代碼草圖:

// the code inside this controller is re-run every time its associated view is activated 
function MyViewController() { 

    /* SNIP (lots of other controller code) */ 

    function myCustomEventHandler() { 
     console.log('myCustomEventHandler has run'); 
     // the code inside this handler requires the controller's scope 
    } 

    // Three variants of the same misbehaving event attachment logic follow: 

    // first attempt 
    $('body').off('myCustomEvent', myCustomEventHandler); 
    $('body').on('myCustomEvent', myCustomEventHandler); 
    // second attempt 
    $('body').one('myCustomEvent', myCustomEventHandler); 
    // third attempt 
    $('body').off('myCustomEvent', $.proxy(myCustomEventHandler, this)); 
    $('body').on('myCustomEvent', $.proxy(myCustomEventHandler, this)); 
    // all of these result in too many event attachments 

}; 

// ...meanwhile, elsewhere in the app, this function is run after a certain user action 
function MyEventSender() { 
    $('body').trigger('myCustomEvent'); 
    console.log('myCustomEvent has been triggered'); 
}; 

點擊左右在我的應用程序切換到麻煩視圖五次,然後做它運行MyEventSender動作後,我的控制檯看起來就像這樣:

myCustomEvent has been triggered 
myCustomEventHandler has run 
myCustomEventHandler has run 
myCustomEventHandler has run 
myCustomEventHandler has run 
myCustomEventHandler has run 

我怎樣才能得到它看起來像這樣:

myCustomEvent has been triggered 
myCustomEventHandler has run 

???

+0

你可以讓你的控制器不做任何事情,如果它不可見?沒有修復你的bug,但它是一種解決問題的不同方式 – 2013-05-10 17:31:56

+0

據我所知,Angular事件週期和jQuery事件週期完全不相關(除了它們都綁定到DOM之外)。您應該在'$(function(){...})'調用中將您的事件附加到控制器外部的標記。 – rossipedia 2013-05-10 17:33:57

+0

適用於我:http://jsbin.com/adecul/1/edit – 2013-05-10 17:34:16

回答

2

你可以竊聽範圍在主控制器破壞事件

function MyViewController($scope) { 
    function myCustomEventHandler() { 
     console.log('myCustomEventHandler has run'); 
     // the code inside this handler requires the controller's scope 
    } 

    $('body').on('myCustomEvent', myCustomEventHandler);  

    $scope.$on("$destroy", function(){ 
     $('body').off('myCustomEvent', myCustomEventHandler); 
     //scope destroyed, no longer in ng view 
    }); 
} 

編輯這是一個angularJS的解決方案。當你逐頁移動時,ngview會不斷加載。隨着功能被反覆調用,它會一遍又一遍地重複這個事件。你想要做的是當有人離開視圖時解除/刪除事件。您可以通過掛鉤到$ destroy(帶有美元符號)事件來執行此操作。你可以在這裏閱讀更多:$destroy docs

+0

我喜歡這個答案,現在我很抱歉我的問題特別提到jQuery。我對jQuery爲什麼沒有按預期工作的好奇心並不完全符合我需要解決的問題,但這個答案很好地解決了我的問題。 – tuff 2013-05-10 20:17:47

+0

這很好,我在回答中提出了這個問題,但是我對角度知之甚少,不瞭解'$ destroy'事件。沒有辦法產生衝突(就像你可以使用事件命名空間一樣) – 2013-05-10 20:26:10

+0

@tuff儘管我不想成爲精英主義者(對不起,如果我是),我會建議我對你選擇的那個回答。原因是,如果你使用頂級的,並且有人進入了視圖,然後你會讓這個事件處理程序仍然綁定在某個地方。即使他們再也不會去看。在我的回答中,它會被永久刪除。 – 2013-05-13 17:22:21

1

問題是,當多次調用function MyViewController(){}時,會得到一個單獨的myCustomEventHandler實例(附加到當前閉包),因此將其傳遞給$.off不會取消註冊前一個處理程序。

KevinB's answer,事件命名空間,是我建議刪除特定的處理程序,而不需要知道哪個處理程序安裝。如果您可以在元素被移除/隱藏時取消註冊事件,那麼會更好,那麼您可以引用要取消註冊的函數,而不必擔心刪除其他代碼可能添加到同一事件名稱空間的處理程序。畢竟,事件名稱空間只是一個全局字符串池,容易受到名稱衝突的影響。

If you make your function global,它也將工作(除非它看起來像你需要關閉),但我只是顯示它說明問題,使用命名空間

function myCustomEventHandler() { 
    console.log('myCustomEventHandler has run'); 
    // the code inside this handler requires the controller's scope 
} 

function MyViewController() { 

    // first attempt 
    $('body').off('myCustomEvent', myCustomEventHandler); 
    $('body').on('myCustomEvent', myCustomEventHandler); 
    // second attempt 
    $('body').one('myCustomEvent', myCustomEventHandler); 
    // third attempt 
    $('body').off('myCustomEvent', $.proxy(myCustomEventHandler, this)); 
    $('body').on('myCustomEvent', $.proxy(myCustomEventHandler, this)); 

} 

// ...meanwhile, elsewhere in the app, this function is run after a certain user action 
function MyEventSender() { 
    $('body').trigger('myCustomEvent'); 
    console.log('myCustomEvent has been triggered'); 
} 
MyViewController(); 
MyViewController(); 
MyEventSender(); 

上一頁理念

其中一個問題是,您沒有將相同的功能傳遞給 $.on$.off,因此off在此情況下不會取消註冊任何內容

不是問題所在,將答案留給參考,因爲它不完全直觀。如果傳遞相同的函數和上下文,$.proxy似乎會返回對相同綁定函數的引用。 http://jsbin.com/adecul/9/edit

+1

這就是我所期望的(我期望看到它執行了3次),而是我只看到它一次,見我的其他評論jsbin。他甚至得到超過三個暗示其他事情正在發生的事情之外 – 2013-05-10 17:38:23

+0

@KevinB我運行了你的jsbin,但我不確定在這種情況下重新加載視圖,我調用了MyEventSender();' – 2013-05-10 17:40:38

+0

是的,沒有我用給定的代碼做的結果在OP看到。 – 2013-05-10 17:41:13

2

給你的事件一個命名空間,然後當你重新運行控制器時,簡單地刪除所有帶有上述命名空間的事件。

jsbin

$('body').off('.controller'); 
$('body').on('myCustomEvent.controller', myCustomEventHandler); 
+0

這可以更容易,然後找出哪些代理處理程序註銷 – 2013-05-10 17:49:17

+0

請參閱我的答案解釋爲什麼OP的代碼不註銷 – 2013-05-10 18:16:15

+0

這不起作用:http://jsbin.com/adecul/17/edit :(我仍然看到「myCustomEventHandler已經運行」x7 – tuff 2013-05-10 20:02:57

相關問題