75

I know that I can get access to the click event from ng-click if I pass in the $event object like so:

<button ng-click="myFunction($event)">Give me the $event</button> <script> function myFunction (event) { typeof event !== "undefined" // true } </script> 

It's a little bit annoying having to pass $event explicitly every time. Is it possible to set ng-click to somehow pass it to the function by default?

13
  • 11
    I know that code is just for demonstration but undefined should be "undefined", otherwise the expression will always evaluate to false as typeof returns a string. Commented Jan 13, 2014 at 21:17
  • 1
    I'm wondering why you need $event anyway. Commented Jan 13, 2014 at 21:28
  • 6
    @zeroflagL Event object is usually used to stop the bubbling/propagation. Commented Jan 13, 2014 at 21:36
  • 3
    @Stewie myFunction is part of a controller. A controller shouldn't care about the UI. Furthermore a simple button has no default action. And angular does prevent many default actions anyway. So while you're right, of course, it's hard to imagine a good use case. Commented Jan 13, 2014 at 21:46
  • 1
    @zeroflagL I have a directive contextMenu which provides several functions for the scope (open, close, toggle), which are called with the name of a specific contextMenu, eg ng-click="account_menu.toggle()". The menu itself is in a different element, eg <div context-menu="account_menu"></div>". In order to position the menu correctly on the page right below the trigger element that called toggle(), I want access to ng-click's $event.target element, so that I can get its coordinates. Do let me know if there is a simpler way :) Commented Jan 13, 2014 at 22:01

4 Answers 4

65

Take a peek at the ng-click directive source:

... compile: function($element, attr) { var fn = $parse(attr[directiveName]); return function(scope, element, attr) { element.on(lowercase(name), function(event) { scope.$apply(function() { fn(scope, {$event:event}); }); }); }; } 

It shows how the event object is being passed on to the ng-click expression, using $event as a name of the parameter. This is done by the $parse service, which doesn't allow for the parameters to bleed into the target scope, which means the answer is no, you can't access the $event object any other way but through the callback parameter.

Sign up to request clarification or add additional context in comments.

Comments

44

Add a $event to the ng-click, for example:

<button type="button" ng-click="saveOffer($event)" accesskey="S"></button> 

Then the jQuery.Event was passed to the callback:

enter image description here

3 Comments

...except that this is the exact thing that the OP is trying to NOT do, unless I'm crazy. The entire point of this post is to access the $event without explicitly passing it... which appears to be exactly what you're doing here.
I (on the other hand) can live with it
Ended up here after a google search for how to pass $event. As similar to other upvoters, didn't read the original question :-|
11

As others said, you can't actually strictly do what you are asking for. That said, all of the tools available to the angular framework are actually available to you as well! What that means is you can actually write your own elements and provide this feature yourself. I wrote one of these up as an example which you can see at the following plunkr (http://plnkr.co/edit/Qrz9zFjc7Ud6KQoNMEI1).

The key parts of this are that I define a "clickable" element (don't do this if you need older IE support). In code that looks like:

<clickable> <h1>Hello World!</h1> </clickable> 

Then I defined a directive to take this clickable element and turn it into what I want (something that automatically sets up my click event):

app.directive('clickable', function() { return { transclude: true, restrict: 'E', template: '<div ng-transclude ng-click="handleClick($event)"></div>' }; }); 

Finally in my controller I have the click event ready to go:

$scope.handleClick = function($event) { var i = 0; }; 

Now, its worth stating that this hard codes the name of the method that handles the click event. If you wanted to eliminate this, you should be able to provide the directive with the name of your click handler and "tada" - you have an element (or attribute) that you can use and never have to inject "$event" again.

Hope that helps!

1 Comment

You have to make that handleClick event something that can be passed in, man :)
2

I wouldn't recommend doing this, but you can override the ngClick directive to do what you are looking for. That's not saying, you should.

With the original implementation in mind:

compile: function($element, attr) { var fn = $parse(attr[directiveName]); return function(scope, element, attr) { element.on(lowercase(name), function(event) { scope.$apply(function() { fn(scope, {$event:event}); }); }); }; } 

We can do this to override it:

// Go into your config block and inject $provide. app.config(function ($provide) { // Decorate the ngClick directive. $provide.decorator('ngClickDirective', function ($delegate) { // Grab the actual directive from the returned $delegate array. var directive = $delegate[0]; // Stow away the original compile function of the ngClick directive. var origCompile = directive.compile; // Overwrite the original compile function. directive.compile = function (el, attrs) { // Apply the original compile function. origCompile.apply(this, arguments); // Return a new link function with our custom behaviour. return function (scope, el, attrs) { // Get the name of the passed in function. var fn = attrs.ngClick; el.on('click', function (event) { scope.$apply(function () { // If no property on scope matches the passed in fn, return. if (!scope[fn]) { return; } // Throw an error if we misused the new ngClick directive. if (typeof scope[fn] !== 'function') { throw new Error('Property ' + fn + ' is not a function on ' + scope); } // Call the passed in function with the event. scope[fn].call(null, event); }); }); }; }; return $delegate; }); }); 

Then you'd pass in your functions like this:

<div ng-click="func"></div> 

as opposed to:

<div ng-click="func()"></div> 

jsBin: http://jsbin.com/piwafeke/3/edit

Like I said, I would not recommend doing this but it's a proof of concept showing you that, yes - you can in fact overwrite/extend/augment the builtin angular behaviour to fit your needs. Without having to dig all that deep into the original implementation.

Do please use it with care, if you were to decide on going down this path (it's a lot of fun though).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.