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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23031381/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 00:12:34  来源:igfitidea点击:

How to safely clean up AngularJS event binding in a directive

javascriptangularjs

提问by Bill Sourour

I have an Angular directive that sets an element's height equal to the inner height of the browser window (+/- a given offset). This directive responds to the window's "resize" event and adjusts its height accordingly. When the scope of my directive emits the '$destory' event, I remove the binding to the "resize" event (I think leaving it in place would cause some issues, correct me if I'm wrong).

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

I don't know how to do this event detachment in a "safe" way. What if I have multiple instances of this directive throughout my app and what if I have other directives that attach to the 'resize' event?

我不知道如何以“安全”的方式进行此事件分离。如果我在整个应用程序中有此指令的多个实例,以及如果我有其他附加到“调整大小”事件的指令怎么办?

JQuery has the concept of event namespace which seems like a good solution, but Angular's implementation (JQLite) does not support this. I'd rather not use JQuery since I'm already using Angular, so what do I do?

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

Here's the code for my directive as it is today

这是我今天的指令的代码

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

UPDATELooks like a case of RTFM, but just in case anyone else wanders in here, here is some more info. Passing the original function (in my case OnResize) to the .off()works to isolate the scope of the .off()function. From the docs:

更新看起来像是 RTFM 的案例,但以防万一其他人在这里闲逛,这里有更多信息。将原始函数(在我的情况下OnResize)传递给.off()作品以隔离.off()函数的范围。从文档

A handler can also be removed by specifying the function name in the handler argument. When jQuery {ahem... JQLite} attaches an event handler, it assigns a unique id to the handler function.

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

Here's the updated cleanUpfunction from my directive:

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

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

Thanks tasseKATT, Karolis and Hans for your contributions.

感谢 tasseKATT、卡罗利斯和汉斯的贡献。

回答by tasseKATT

Pass the same function reference to offas you pass to on:

通过同样的功能引用off,你传递给on

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

Instead of:

代替:

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

Demo - Passing function reference to off: http://plnkr.co/edit/1rfVPNXl6TrEcuYvzPAj?p=preview

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

Demo - Not passing function reference to off: http://plnkr.co/edit/IsLqSLAzNcHqDnhMty7Q?p=preview

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

The demos contain two directives both listening to the window resize event. Use the vertical separator between the code and the preview to trigger the event.

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

You will notice that if you destroy one the other will keep working when passing function reference to off. If you don't, both will stop working.

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

回答by Hans

I had the same question a few weeks ago.

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

After looking through the jqLite source (https://github.com/angular/angular.js/blob/master/src/jqLite.js), we see that the onmethod adds the event and the offmethod removes the event via the jqLiteOfffunction.

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

Looking deeper, we see jqLiteRemoveDatacalls jqLiteOff. jqLiteRemoveDatais called by jqLiteDealoc. jqLiteDealocgets called in a few places: jqLiteEmpty, html, replaceWith, and remove. jqLiteEmptygets assigned to the element's emptymethod, which clears the element in jQuery. html, replaceWithand removeare jQuery mimics.

更深入地看,我们看到了jqLiteRemoveDatacall jqLiteOffjqLiteRemoveData由 调用jqLiteDealocjqLiteDealoc被调用在几个地方:jqLiteEmptyhtmlreplaceWith,和removejqLiteEmpty分配给元素的empty方法,该方法清除 jQuery 中的元素。html,replaceWith并且remove是 jQuery 模仿。

Doing a search on where remove()is called on an element, we see that it is used on most, if not all, DOM manipulation logic. You can see it in ngIf, ngSwitch, ngIncludeand ngView.

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

So I think Angular does handle event listener cleanup, as long as you use jqLite to attach events and call remove()appropriately in your own DOM manipulation logic. Using jQuery to wrap an element would mess up a lot of processes, including event listener cleanup, but I guess you are already fully aware of that since you are using angular.element.

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

回答by Karolis Juodel?

For one, there is absolutely nothing wrong with using JQuery and AngularJS together.

一方面,一起使用 JQuery 和 AngularJS 绝对没有错。

That aside, what I like to do is have a bodydirective which listens to window.on('resize', ...)and writes the size into $rootScope.windowSize. Then have another directive on the element, to $watch("windowSize", ...)and set to width as needed. (You don't actually have to expose the size in $scope- you can instead use require).

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