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-directivewhen angular treats following equivalent:my:directive,data-my-directive,x-my-directive,my_directivecan seen in guide directivesyou have single place affect how javascript event converted angular event, affects listeners. later want debounce javascript
resizeevent, using lodash debounce function. amendresizedirective 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 putresizedirective<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