javascript - AngularJS : What is the best way to bind to a global event in a directive -
imagine situation in angularjs want create directive needs respond global event. in case, let's say, window resize event.
what best approach this? way see it, have 2 options: 1. let every directive bind event , it's magic on current element 2. create global event listener dom selector each element on logic should applied.
option 1 has advantage have access element on want operations. but...options 2 has advantage not have bind multiple times (for each directive) on same event can performance benefit.
let's illustrate both options:
option 1:
angular.module('app').directive('mydirective', function(){ function dosomethingfancy(el){ // in here have our operations on element } return { link: function(scope, element){ // bind window resize event each directive instance. angular.element(window).on('resize', function(){ dosomethingfancy(element); }); } }; });
option 2:
angular.module('app').directive('mydirective', function(){ function dosomethingfancy(){ var elements = document.queryselectorall('[my-directive]'); angular.foreach(elements, function(el){ // in here have our operations on element }); } return { link: function(scope, element){ // maybe have in here, maybe not. } }; // bind window resize event once. angular.element(window).on('resize', dosomethingfancy); });
both approaches working fine feel option 2 not 'angular-ish'.
any ideas?
i have chosen method, localise global events, window resizing. converts javascript events angular scope events, via directive.
app.directive('resize', function($window) { return { link: function(scope) { function onresize(e) { // namespacing events name of directive + event 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 used, in basic case, on root element of app
<body ng-app="myapp" resize>...
and listen event in other directives
<div my-directive>....
coded as:
app.directive('mydirective', function() { return { link: function(scope, element) { scope.$on('resize::resize', function() { dosomethingfancy(element); }); }); } });
this has number of benefits on other approaches:
not brittle exact form on how directives used. option 2 requires
my-directive
when angular treats following equivalent:my:directive
,data-my-directive
,x-my-directive
,my_directive
can seen in guide directivesyou have single place affect how javascript event converted angular event, affects listeners. later want debounce javascript
resize
event, using lodash debounce function. amendresize
directive to:angular.element($window).on('resize', $window._.debounce(function() { scope.$broadcast('resize::resize'); },500));
because doesn't fire events on
$rootscope
, can restrict events part of app moving putresize
directive<body ng-app="myapp"> <div> <!-- no 'resize' events here --> </div> <div resize> <!-- 'resize' events $broadcast here --> </div>
you can extend directive options, , use differently in different parts of app. 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)); }
used as:
<div resize> <!-- undebounced 'resize' angular events here --> </div> <div resize="500"> <!-- 'resize' debounced 500 milliseconds --> </div>
you can later extend directive other events might useful. maybe things
resize::heightincrease
.resize::heightdecrease
,resize::widthincrease
,resize::widthdecrease
. have 1 place in app deals remembering , processing exact dimensions of window.you can pass data along events. viewport height/width might need deal cross-browser issues (depending on how far need ie support, , whether include library you).
angular.element($window).on('resize', function() { // 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 single place add data later. e.g. want send difference in dimensions since last debounced event? add bit of code remember old size , send difference.
essentially design provides way convert, in configurable manner, global javascript events local angular events, , local not app, local different parts of app, depending on placement of directive.
Comments
Post a Comment