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.
What
When developing on top of a framework, I often find it helpful to think agnostically about a problem before designing an idiomatic. Answering the "what" and the "why" drives out the "how".
The answer here really depends on the complexity of doSomethingFancy()
. Is there data, a set of functionality, or domain object(s) associated with instances of this directive? Is it a purely presentational concern, like adjusting the width
or height
properties of certain elements to appropriate proportions of the window size? Make sure you're using the right tool for the job; don't bring the whole Swiss Army knife when the job calls for tweezers and you have access to a standalone pair. For sake of continuing in this vein, I'm going to operate with the assumption that doSomethingFancy()
is a purely presentational function.
The concern of wrapping a global browser event in an Angular event could be handled by some simple run phase configuration:
angular.module('myApp')
.run(function ($rootScope) {
angular.element(window).on('resize', function () {
$rootScope.$broadcast('global:resize');
})
})
;
Now Angular doesn't have to do all of the work associated with a directive on each $digest
,
but you're getting the same functionality.
The second concern is operating on n
number of elements when this event is fired. Again, if you don't need all of the bells and whistles of a directive, there are other ways to make this happen. You could expand or adapt the approach in the run block above:
angular.module('myApp')
.run(function () {
angular.element(window).on('resize', function () {
var elements = document.querySelectorAll('.reacts-to-resize');
})
})
;
If you do have more complex logic that needs to happen on the resize event, it still does not necessarily mean that one or more directives are the best way to handle it. You could use a simple mediator service that gets instantiated instead of the aforementioned anonymous run phase configuration:
/**
* you can inject any services you want: $rootScope if you still want to $broadcast (in)
* which case, you'd have a "Publisher" instead of a "Mediator"), one or more services
* that maintain some domain objects that you want to manipulate, etc.
*/
function ResizeMediator($window) {
function doSomethingFancy() {
// whatever fancy stuff you want to do
}
angular.element($window).bind('resize', function () {
// call doSomethingFancy() or maybe some other stuff
});
}
angular.module('myApp')
.service('resizeMediator', ResizeMediator)
.run(resizeMediator)
;
Now we have an encapsulated service that can be unit tested, but doesn't run unused execution phases.
A couple concerns that would also factor into the decision:
$on('$destroy')
, you're running the risk of event handlers applying themselves when their elements no longer exist.It's likely that this answer is not as "Angular" as you were hoping for, but it's the way I'd solve the problem as I understand it with the added assumption of box-model-only logic.