如何安全地清理指令中的AngularJS事件绑定

How to safely clean up AngularJS event binding in a directive

本文关键字:指令 AngularJS 绑定 事件 何安全 安全      更新时间:2023-09-26

我有一个Angular指令,它将元素的高度设置为浏览器窗口的内部高度(+/-给定的偏移量)。此指令响应窗口的"调整大小"事件并相应地调整其高度。当我的指令的作用域发出"$destory"事件时,我会删除与"resize"事件的绑定(我认为保留它会导致一些问题,如果我错了,请纠正我)。

我不知道如何以一种"安全"的方式完成这项活动。如果我在整个应用程序中有多个该指令的实例,如果我有附加到"调整大小"事件的其他指令,该怎么办?

JQuery有事件名称空间的概念,这似乎是一个很好的解决方案,但Angular的实现(JQLite)不支持这一点。我宁愿不使用JQuery,因为我已经在使用Angular了,那么我该怎么办呢?

这是我指令的代码,今天是

window.angular.module('arcFillClient', [])
    .directive('arcFillClientY', ['$window',
        function ($window) {
            function link($scope, el, attrs) {
                var setHeight,
                    onResize,
                    cleanUp;
                setHeight = function (offSetY) {
                    var newHeight;
                    offSetY = offSetY || 0;
                    newHeight = Math.max($window.innerHeight + parseInt(offSetY, 10)) + 'px';
                    el.height(newHeight);
                };
                onResize = function () {
                    var offset;
                    offset = attrs.arcFillClientY || 0;
                    setHeight(offset);
                };
                attrs.$observe('arcFillClientY', setHeight);
                window.angular.element($window).on('resize', onResize);
                cleanUp = function () {
                    window.angular.element($window).off('resize');
                };
                $scope.$on('$destroy', cleanUp);
            }
            return {
                link: link
            };

UPDATE看起来像是RTFM的一个例子,但为了防止其他人在这里闲逛,这里有一些更多信息。将原始函数(在我的例子中是OnResize)传递给.off()可以隔离.off()函数的作用域。来自文档:

也可以通过在处理程序参数中指定函数名来删除处理程序。当jQuery{ahem…JQLite}附加一个事件处理程序时,它会为处理程序函数分配一个唯一的id。

这是我指令中更新的cleanUp函数:

cleanUp = function () {
    window.angular.element($window).off('resize', onResize);
};

感谢tasseKATT、Karolis和Hans的贡献。

off传递与传递给on:相同的函数引用

window.angular.element($window).off('resize', onResize);

代替:

window.angular.element($window).off('resize');

演示-将函数引用传递到off:http://plnkr.co/edit/1rfVPNXl6TrEcuYvzPAj?p=preview

演示-不将函数引用传递到off:http://plnkr.co/edit/IsLqSLAzNcHqDnhMty7Q?p=preview

演示包含两个指令,这两个指令都在侦听窗口大小调整事件。在代码和预览之间使用垂直分隔符来触发事件。

你会注意到,如果你破坏了其中一个,那么在将函数引用传递到off时,另一个将继续工作。如果你不这样做,两者都将停止工作。

几周前我也有同样的问题。

查看jqLite源代码后(https://github.com/angular/angular.js/blob/master/src/jqLite.js),我们看到on方法通过jqLiteOff函数添加事件,off方法删除事件。

更深入地看,我们看到jqLiteRemoveData调用jqLiteOff。CCD_ 12由CCD_。jqLiteDealoc在几个地方被调用:jqLiteEmptyhtmlreplaceWithremovejqLiteEmpty被分配给元素的empty方法,该方法清除jQuery中的元素。htmlreplaceWithremove是jQuery模拟。

搜索元素上调用remove()的位置,我们可以看到它被用于大多数(如果不是全部的话)DOM操作逻辑。您可以在ngIfngSwitchngIncludengView中看到它。

因此,我认为Angular确实可以处理事件侦听器清理,只要您使用jqLite附加事件,并在自己的DOM操作逻辑中适当地调用remove()。使用jQuery包装一个元素会打乱很多过程,包括事件侦听器清理,但我想您已经完全意识到了这一点,因为您使用的是angular.element

首先,将JQuery和AngularJS一起使用绝对没有错。

除此之外,我喜欢做的是有一个body指令,它侦听window.on('resize', ...)并将大小写入$rootScope.windowSize。然后在元素上有另一个指令,即$watch("windowSize", ...),并根据需要设置为width。(实际上不必在$scope中公开大小,而是可以使用require)。