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
How to safely clean up AngularJS event binding in a directive
提问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 cleanUp
function 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 off
as 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
演示 - 将函数引用传递给 off:http: //plnkr.co/edit/1rfVPNXl6TrEcuYvzPAj?p=preview
Demo - Not passing function reference to off: http://plnkr.co/edit/IsLqSLAzNcHqDnhMty7Q?p=preview
演示 - 未将函数引用传递给 off:http: //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 on
method adds the event and the off
method removes the event via the jqLiteOff
function.
通过查看jqLite源码(https://github.com/angular/angular.js/blob/master/src/jqLite.js),我们看到on
方法是添加事件,off
方法是通过jqLiteOff
函数删除事件。
Looking deeper, we see jqLiteRemoveData
calls jqLiteOff
. jqLiteRemoveData
is called by jqLiteDealoc
. jqLiteDealoc
gets called in a few places: jqLiteEmpty
, html
, replaceWith
, and remove
. jqLiteEmpty
gets assigned to the element's empty
method, which clears the element in jQuery. html
, replaceWith
and remove
are jQuery mimics.
更深入地看,我们看到了jqLiteRemoveData
call jqLiteOff
。jqLiteRemoveData
由 调用jqLiteDealoc
。jqLiteDealoc
被调用在几个地方:jqLiteEmpty
,html
,replaceWith
,和remove
。jqLiteEmpty
分配给元素的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
, ngInclude
and ngView
.
搜索remove()
元素上的 where调用,我们看到它被用于大多数(如果不是全部)DOM 操作逻辑。你可以看到它ngIf
,ngSwitch
,ngInclude
和ngView
。
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 确实会处理事件监听器清理,只要你使用 jqLite 来附加事件并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 body
directive 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
)。