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

时间:2014-04-12 14:02:02

标签: javascript angularjs

我有一个Angular指令,它将元素的高度设置为等于浏览器窗口的内部高度(+/-给定的偏移量)。该指令响应窗口"调整大小"事件并相应调整其高度。当我的指令范围发出' $ destory'事件,我删除绑定到"调整大小"事件(我认为将它留在原地会引起一些问题,如果我错了,请纠正我)。

我不知道如何在" safe"中做这个事件分离。办法。如果我在我的应用程序中有多个此指令实例,如果我有其他指令附加到'调整大小,该怎么办?事件?

JQuery具有事件命名空间的概念,这似乎是一个很好的解决方案,但Angular的实现(JQLite)does not support this。我不想使用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
            };

更新看起来像RTFM的情况,但万一其他人徘徊在这里,这里有更多信息。将原始函数(在我的情况下为OnResize)传递给.off()可以隔离.off()函数的范围。来自docs

  

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

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

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

感谢tasseKATT,Karolis和Hans的贡献。

3 个答案:

答案 0 :(得分:28)

在传递给off时将相同的函数引用传递给on

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

而不是:

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

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

演示 - 未将函数引用传递给关闭http://plnkr.co/edit/IsLqSLAzNcHqDnhMty7Q?p=preview

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

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

答案 1 :(得分:5)

几个星期前,我有同样的问题。

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

仔细观察,我们看到jqLiteRemoveData来电jqLiteOffjqLiteRemoveData调用jqLiteDealocjqLiteDealoc会在jqLiteEmptyhtmlreplaceWithremove中调用jqLiteEmptyempty被赋值给元素的html方法,该方法清除了jQuery中的元素。 replaceWithremoveremove()是jQuery模仿。

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

所以我认为Angular会处理事件监听器清理,只要你使用jqLit​​e附加事件并在你自己的DOM操作逻辑中适当地调用angular.element。使用jQuery包装元素会搞砸很多进程,包括事件监听器清理,但我想你已经完全清楚了,因为你使用的是{{1}}。

答案 2 :(得分:2)

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

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