3

我的页面中有一大块表示视图的标记,以及与该视图关联的 JS 控制器函数。(这些是 Angular,但我认为这并不重要。)控制器代码监听从应用程序其他地方触发的自定义事件,并使用一些特定于控制器的逻辑处理该事件。

我的问题是控制器的事件处理程序被附加太多次:每次重新激活视图时都会附加它,导致每次触发自定义事件时处理程序都会运行多次。我只希望处理程序在每个事件中运行一次。

我尝试.off()在绑定之前使用取消绑定处理程序;我试图.one()确保处理程序只运行一次;我$.proxy()在阅读了它与.off() 这里的交互后尝试过。

这是我的代码的草图:

// 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

???

4

3 回答 3

3

给你的事件一个命名空间,然后在你重新运行控制器时简单地删除所有具有所述命名空间的事件。

jsbin

$('body').off('.controller');
$('body').on('myCustomEvent.controller', myCustomEventHandler);
于 2013-05-10T17:47:58.367 回答
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

于 2013-05-10T18:25:31.507 回答
2

问题是,当function MyViewController(){}被多次调用时,您会获得一个单独的实例myCustomEventHandler(附加到当前闭包),因此将其传递给$.off不会取消注册前一个处理程序。

KevinB 的回答,事件命名空间,是我建议删除特定处理程序而不需要知道安装了哪个处理程序。如果您可以在删除/隐藏元素时取消注册事件会更好,那么您将拥有对要取消注册的函数的引用,而不会冒删除其他代码可能已添加到同一事件命名空间的处理程序的风险。毕竟,事件命名空间只是一个全局字符串池,容易发生名称冲突。

如果你让你的函数 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();

以前的想法

问题之一是您没有将相同的函数传递给$.onand $.off,因此在这种情况下 off 不会取消注册任何内容

不是问题,将答案留作参考,因为它并不完全直观。$.proxy如果传递相同的函数和上下文,似乎返回对相同绑定函数的引用。http://jsbin.com/adecul/9/edit

于 2013-05-10T17:35:19.427 回答