想象一下 AngularJS 中的情况,您想要创建一个需要响应全局事件的指令。在这种情况下,假设是窗口调整大小事件。
最好的方法是什么?在我看来,我们有两个选择: 1. 让每个指令绑定(bind)到事件上,并对当前元素施展魔法 2. 创建一个全局事件监听器,它执行 DOM 选择器以获取应应用逻辑的每个元素。
选项 1 的优点是您已经可以访问要对其执行某些操作的元素。但是...选项 2 的优点是您不必在同一事件上多次绑定(bind)(针对每个指令),这可能会提高性能。
让我们来说明这两个选项:
选项 1:
angular.module('app').directive('myDirective', function(){
function doSomethingFancy(el){
// In here we have our operations on the element
}
return {
link: function(scope, element){
// Bind to the window resize event for each directive instance.
angular.element(window).on('resize', function(){
doSomethingFancy(element);
});
}
};
});
选项 2:
angular.module('app').directive('myDirective', function(){
function doSomethingFancy(){
var elements = document.querySelectorAll('[my-directive]');
angular.forEach(elements, function(el){
// In here we have our operations on the element
});
}
return {
link: function(scope, element){
// Maybe we have to do something in here, maybe not.
}
};
// Bind to the window resize event only once.
angular.element(window).on('resize', doSomethingFancy);
});
这两种方法都运行良好,但我觉得选项二并不是真正的“Angular-ish”。
有什么想法吗?
最佳答案
我选择了另一种方法来有效地本地化全局事件,例如调整窗口大小。它通过另一个指令将 Javascript 事件转换为 Angular 作用域事件。
app.directive('resize', function($window) {
return {
link: function(scope) {
function onResize(e) {
// Namespacing events with name of directive + event to avoid collisions
scope.$broadcast('resize::resize');
}
function cleanUp() {
angular.element($window).off('resize', onResize);
}
angular.element($window).on('resize', onResize);
scope.$on('$destroy', cleanUp);
}
}
});
在基本情况下,可以在应用程序的根元素上使用
<body ng-app="myApp" resize>...
然后在其他指令中监听事件
<div my-directive>....
编码为:
app.directive('myDirective', function() {
return {
link: function(scope, element) {
scope.$on('resize::resize', function() {
doSomethingFancy(element);
});
});
}
});
与其他方法相比,这有很多好处:
对如何使用指令的确切形式不敏感。当 Angular 将以下内容视为等效时,您的选项 2 需要
my-directive
:my:directive
、data-my-directive
、x -my-directive
,my_directive
可以在guide for directives中看到您只有一个地方可以准确影响 Javascript 事件如何转换为 Angular 事件,然后影响所有监听器。假设您稍后想要使用 Lodash debounce function 去抖动 javascript
resize
事件.您可以将resize
指令修改为:angular.element($window).on('resize', $window._.debounce(function() { scope.$broadcast('resize::resize'); },500));
因为它不一定会触发
$rootScope
上的事件,所以您可以通过移动放置resize的位置来将事件限制为仅应用程序的一部分
指令<body ng-app="myApp"> <div> <!-- No 'resize' events here --> </div> <div resize> <!-- 'resize' events are $broadcast here --> </div>
您可以使用选项扩展指令,并在应用的不同部分以不同方式使用它。假设您想要在不同部分使用不同的去抖动版本:
link: function(scope, element, attrs) { var wait = 0; attrs.$observe('resize', function(newWait) { wait = $window.parseInt(newWait || 0); }); angular.element($window).on('resize', $window._.debounce(function() { scope.$broadcast('resize::resize'); }, wait)); }
用作:
<div resize> <!-- Undebounced 'resize' Angular events here --> </div> <div resize="500"> <!-- 'resize' is debounced by 500 milliseconds --> </div>
您稍后可以使用其他可能有用的事件来扩展该指令。也许像
resize::heightIncrease
这样的东西。resize::heightDecrease
,resize::widthIncrease
,resize::widthDecrease
。然后,您的应用程序中有一个地方负责内存和处理窗口的确切尺寸。您可以将数据与事件一起传递。比如说您可能需要处理跨浏览器问题的视口(viewport)高度/宽度(取决于您需要多远的 IE 支持,以及您是否包含另一个库来帮助您)。
angular.element($window).on('resize', function() { // From http://stackoverflow.com/a/11744120/1319998 var w = $window, d = $document[0], e = d.documentElement, g = d.getElementsByTagName('body')[0], x = w.innerWidth || e.clientWidth || g.clientWidth, y = w.innerHeight|| e.clientHeight|| g.clientHeight; scope.$broadcast('resize::resize', { innerWidth: x, innerHeight: y }); });
这让您可以在一个地方稍后添加数据。例如。假设您想发送自上次去抖事件以来的尺寸差异?您可能会添加一些代码来记住旧尺寸并发送差异。
本质上,这种设计提供了一种以可配置的方式将全局 Javascript 事件转换为本地 Angular 事件的方法,并且不仅适用于应用程序,还适用于应用程序的不同部分,具体取决于指令的位置。
关于javascript - Angular : What is the best way to bind to a global event in a directive,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23272169/