An Angular 'Any Other Click' Directive
I recently implemented a very plain popup menu in Angular. Showing the menu was simple, as you would expect, but dismissing the menu threw me for a bit of a loop. How could I have my menu respond to a click event outside of the element hosting its directive? After a bit of thought, I wrote the following ‘any other click’ directive. Basically, the directive registers a click event on the document element. This click event checks to see if it was generated from the element that registered the directive. If not, it executes the code specified in the directive attribute. jQuery is required since angular.element.find() is limited to lookups by tag name.
Directive Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(function () {
var module = angular.module('anyOtherClick', []);
var directiveName = "anyOtherClick";
module.directive(directiveName, ['$document', "$parse", function ($document, $parse) {
return {
restrict: 'A',
link: function (scope, element, attr, controller) {
var anyOtherClickFunction = $parse(attr[directiveName]);
var documentClickHandler = function (event) {
var eventOutsideTarget = (element[0] !== event.target) && (0 === element.find(event.target).length);
if (eventOutsideTarget) {
scope.$apply(function () {
anyOtherClickFunction(scope, {});
});
}
};
$document.on("click", documentClickHandler);
scope.$on("$destroy", function () {
$document.off("click", documentClickHandler);
});
},
};
}]);
})();
Plunker Demo
Items of Interest
- Angular exposes an
$eventobject duringng-click. We use$event.stopPropagation()in theng-clickexpression that shows the popup to prevent the popup’sany-other-clickexpression from firing and immediately hiding the popup. - As noted above, jQuery is required since
angular.element.find()is limited to lookups by tag name. Alternative pure-Angular suggestions would be greatly appreciated.
Disclaimer
The code above is working for me in production. However, I only use this directive a few times on my pages. As with most things in Angular, you’d need to re-evaluate this directive’s performance if it is used in a large ng-repeat or other looping constructs.
Update - 2015.01.07
I have updated the directive to avoid creating an isolate scope based on the ng-click source. This lets the directive play nice with other angular directives.
comments powered by Disqus