EDIT: There were a couple of problems in this old, old answer.
*Also: Marking Community Wiki (no points for me) because errors
-
N calls for N uses of the directive. This probably isn’t desirable for uses within the same scope with matching expressions.
-
NOTHING WAS TEARING DOWN THE EVENT HANDLERS!!!! BAD! BAD! BAD!
So, I’m updating this answer. Hopefully it didn’t cause anyone too much trouble.
Updated answer
Here’s a new plunker with those issues fixed … there are likely other things that individual application developers will run into. This is just an example of how to handle this problem.
app.factory('clickAnywhereButHereService', function($document){
var tracker = [];
return function($scope, expr) {
var i, t, len;
for(i = 0, len = tracker.length; i < len; i++) {
t = tracker[i];
if(t.expr === expr && t.scope === $scope) {
return t;
}
}
var handler = function() {
$scope.$apply(expr);
};
$document.on('click', handler);
// IMPORTANT! Tear down this event handler when the scope is destroyed.
$scope.$on('$destroy', function(){
$document.off('click', handler);
});
t = { scope: $scope, expr: expr };
tracker.push(t);
return t;
};
});
app.directive('clickAnywhereButHere', function($document, clickAnywhereButHereService){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
var handler = function(e) {
e.stopPropagation();
};
elem.on('click', handler);
scope.$on('$destroy', function(){
elem.off('click', handler);
});
clickAnywhereButHereService(scope, attr.clickAnywhereButHere);
}
};
});
Original answer (with fixes for teardown of event handlers)
You were close with the one answer you’ve found, but I’ve put together a plunk for you to show you what it was missing.
app.directive('clickAnywhereButHere', function($document){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
var elemClickHandler = function(e) {
e.stopPropagation();
};
var docClickHandler = function() {
scope.$apply(attr.clickAnywhereButHere);
};
elem.on('click', elemClickHandler);
$document.on('click', docClickHandler);
// teardown the event handlers when the scope is destroyed.
scope.$on('$destroy', function() {
elem.off('click', elemClickHandler);
$document.off('click', docClickHandler);
});
}
}
})
HTML
<a click-anywhere-but-here="clickedSomewhereElse()"
ng-click="clickedHere()">Don't Click Me!</a>