AngularJS:在指令中绑定全局事件的最佳方法是什么 [英] AngularJS : What is the best way to bind to a global event in a directive

查看:73
本文介绍了AngularJS:在指令中绑定全局事件的最佳方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下AngularJS中的情况,您想要创建一个需要响应全局事件的指令.例如,在这种情况下,窗口调整大小事件.

Imagine the situation in AngularJS where you want to create a directive that needs to respond to a global event. In this case, let's say, the window resize event.

什么是最好的方法?从我的角度来看,我们有两种选择: 1.让每个指令都绑定到该事件,并在当前元素上做到神奇 2.创建一个全局事件侦听器,该侦听器执行DOM选择器以获取应在其上应用逻辑的每个元素.

What is the best approach for this? The way I see it, we have two options: 1. Let every directive bind to the event and do it's magic on the current element 2. Create a global event listener that does a DOM selector to get each element on which the logic should be applied.

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

Option 1 has the advantage that you already have access to the element on which you want to do some operations. But...options 2 has the advantage that you do not have to bind multiple times (for each directive) on the same event which can be a performance benefit.

让我们说明两个选项:

选项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".

Both approaches are working fine but I feel that option two is not really 'Angular-ish'.

有什么想法吗?

推荐答案

我选择了另一种方法来有效地定位全局事件,例如调整窗口大小.它将通过另一个指令将Javascript事件转换为Angular作用域事件.

I have chosen another method, to effectively localise global events, like window resizing. It converts Javascript events to Angular scope events, via another directive.

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

在基本情况下,可以在应用程序的根元素上使用哪些

Which can be used, in the basic case, on the root element of the app

<body ng-app="myApp" resize>...

然后在其他指令中监听事件

And then listen for the event in other directives

<div my-directive>....

编码为:

app.directive('myDirective', function() {
  return {
    link: function(scope, element) {
      scope.$on('resize::resize', function() {
        doSomethingFancy(element);
      });
    });
  }
});

与其他方法相比,它具有许多优点:

This has a number of benefits over other approaches:

  • 对于使用指令的确切形式并不脆弱.您的选择2要求my-directive,当angular将以下内容等效时:my:directivedata-my-directivex-my-directivemy_directive,如在

  • Not brittle to the exact form on how directives are used. Your Option 2 requires my-directive when angular treats the following as equivalent: my:directive, data-my-directive, x-my-directive, my_directive as can be seen in the guide for directives

您只有一个地方可以确切影响Javascript事件如何转换为Angular事件,从而影响所有侦听器.假设您以后想使用 Lodash防反跳功能对javascript resize事件进行防反跳.您可以将resize指令修改为:

You have a single place to affect exactly how the Javascript event is converted to the Angular event, which then affects all listeners. Say you later want to debounce the javascript resize event, using the Lodash debounce function. You could amend the resize directive to:

angular.element($window).on('resize', $window._.debounce(function() {
  scope.$broadcast('resize::resize');
},500));

  • 因为它不一定会触发$rootScope上的事件,所以您只需将resize指令的位置移动到其他位置,就可以将事件限制为仅应用程序的一部分

  • Because it doesn't necessarily fire the events on $rootScope, you can restrict the events to only part of your app just by moving where you put the resize directive

    <body ng-app="myApp">
      <div>
        <!-- No 'resize' events here -->
      </div>
      <div resize>
        <!-- 'resize' events are $broadcast here -->
      </div>
    

  • 您可以使用选项扩展指令,并在应用程序的不同部分中以不同的方式使用它.假设您要在不同部分使用不同的防反跳版本:

  • You can extend the directive with options, and use it differently in different parts of your app. Say you want different debounced versions in different parts:

    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.然后,您的应用程序中就有一个位置可以用来记住和处理窗口的确切尺寸.

  • You can later extend the directive with other events that might be useful. Maybe things like resize::heightIncrease. resize::heightDecrease, resize::widthIncrease, resize::widthDecrease. You then have one place in your app that deals with remembering and processing the exact dimensions of the window.

    您可以将数据与事件一起传递.比如说视口高度/宽度,您可能需要在其中处理跨浏览器的问题(取决于您需要IE支持多久了,以及是否包括其他可以帮助您的库).

    You can pass data along with the events. Say like the viewport height/width where you might need to deal with cross-browser issues (depending on how far back you need IE support, and whether you include another library to help you).

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

    ,使您可以在以后添加到数据的单个位置.例如.说您要发送自上次去抖动事件以来的尺寸差异?您可能可以添加一些代码来记住旧的大小并发送差异.

    which gives you a single place to add to the data later. E.g. say you want to send the difference in dimensions since the last debounced event? You could probably add a bit of code to remember the old size and send the difference.

    从本质上讲,此设计提供了一种以可配置方式将全局Javascript事件转换为本地Angular事件的方法,并且不仅对应用程序本地,而且对应用程序的不同部分都本地化(取决于指令的位置).

    Essentially this design provides a way to convert, in a configurable manner, global Javascript events to local Angular events, and local not just to an app, but local to different parts of an app, depending on the placement of the directive.

    这篇关于AngularJS:在指令中绑定全局事件的最佳方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆