javascript – AngularJS:绑定到指令中的全局事件的最好方法是什么

想象一下AngularJS的情况,你想创建一个需要响应全局事件的指令。在这种情况下,让我们说,窗口调整大小事件。

这是什么最好的方法?我看到它的方式,我们有两个选择:
让每个指令绑定到事件,并在当前元素上做它的魔术
2.创建一个全局事件侦听器,它做一个DOM选择器来获取应该应用逻辑的每个元素。

选项1的优点是,您已经可以访问要对其执行某些操作的元素。但…选项2的优点是,你不必绑定多次(对于每个指令)在同一事件,这可以是一个性能的好处。

让我们来说明这两个选项:

选项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);
});

两种方法都很好,但我觉得选项二不是真正的“角度”。

有任何想法吗?

最佳答案
我选择了另一种方法,以有效地本地化全局事件,如窗口调整大小。它通过另一个指令将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);
      });
    });
  }
});

这比其他方法有许多好处:

>不脆弱到如何使用指令的确切形式。你的选项2需要my-directive,当角度对待下面的等价物:my:directive,data-my-directive,x-my-directive,my_directive,可以在guide for directives看到
>你有一个地方可以精确地影响Javascript事件如何转换为Angular事件,然后会影响所有侦听器。说你以后想要去掉javascript的resize事件,使用Lodash debounce function.你可以修改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。然后你的应用程序中有一个地方处理记住和处理窗口的确切尺寸。
>您可以随事件传递数据。像视口高度/宽度,你可能需要处理跨浏览器问题(取决于你需要IE支持多远,以及是否包括另一个库来帮助你)。

angular.element($window).on('resize', function() {
  // From https://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 – AngularJS:绑定到指令中的全局事件的最好方法是什么 - 代码日志