javascript - Angular : What is the best way to bind to a global event in a directive

标签 javascript angularjs events resize directive

想象一下 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:directivedata-my-directivex -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::heightDecreaseresize::widthIncreaseresize::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/

相关文章:

javascript - 在 TypeScript 中编写 npm 模块

javascript - 单选按钮上需要

javascript - react setState : using checkbox to change boolean value

javascript - Node.js Web 服务是什么样的?

javascript - 为什么我的 $state.go 只能在 setTimeout 内工作?

javascript - 添加动态元素时进行 AngularJS 表单验证

angularjs - typescript : AngularJS retrieving a result object

div 子级的 jQuery 调整大小事件

javascript - 向下滚动触发动画

javascript - 调用jquery内部定义的javascript方法