1

Why does angular use an ng-click="f()" attribute for handling click events, instead of $(el).on('click', f)?

I'm asking this because it's generally considered a bad practice to use the html onclick attribute, so why does Angular use this approach?

ram1993
  • 979
  • 1
  • 9
  • 13
qwertytrewq
  • 189
  • 3
  • 13
  • 1
    That "duplicate" does not answer this question at all. – Pointy Oct 12 '16 at 19:22
  • see [my answer](http://stackoverflow.com/a/40006791/2545680), I've tried to provide explanation regarding how those two attributes are processed – Max Koretskyi Oct 12 '16 at 19:28
  • 3
    I think there's some ambiguity to what is being asked. I'm reading this as *"why does angular's approach not violate the unobtrusive JS mantra"*. Don't know if that's the intent. –  Oct 12 '16 at 19:29
  • 1
    @squint, based on the OP's reputation, I believe he thinks that both are a general way to add event listeners – Max Koretskyi Oct 12 '16 at 19:31
  • @Maximus: I don't understand what rep has to do with it, but both are a general way, if by "both" you mean the HTML attribute and the `.on('click', myFunction)`, seeing that as basically `addEventListener`. The Angular syntax is certainly framework-specific. –  Oct 12 '16 at 19:34
  • Given the OP's latest edit, it seems it is a question of style/philosophy. I wouldn't be too quick to assume that low-rep means low-experience. –  Oct 12 '16 at 19:39
  • 1
    @qwertytrewq: The question is a matter of opinion but what the question implies is valid. There are a number of things that people go around shouting *"bad practice"* at but then do effectively the same thing in a different form. It's really pretty bizarre. –  Oct 12 '16 at 19:42
  • 1
    @squint Yes it's a question of style/philosophy. And btw I have some experience with html, js and css, but I just started using the Angular (Material) library, so I don't have any experience with Anglar – qwertytrewq Oct 12 '16 at 19:42
  • 1
    @squint, yeah, generally users with good-experience formualate questions pretty straightforward and clear. Since this one triggered confusion, I concluded low-experience – Max Koretskyi Oct 12 '16 at 19:43
  • 1
    @squint I completely agree with that! – qwertytrewq Oct 12 '16 at 19:44
  • @squint My unclear formulation/explanation is probably because of my bad English. Sorry for that – qwertytrewq Oct 12 '16 at 19:45
  • Note that *internally* it's pretty likely that Angular does *not* use the "old fashioned" attribute/property way of managing the actual event handlers, and that it instead uses a mechanism fairly similar to what jQuery and other common libraries/frameworks use (the `addEventListener()` API). – Pointy Oct 12 '16 at 19:45
  • @qwertytrewq: FWIW, I didn't think it was all that unclear. The users who originally closed it as a duplciate [of this question](http://stackoverflow.com/questions/14793692/angulars-use-of-inline-javascript-in-html-attributes-is-not-bad-practice) seemed to understand what you were asking too. –  Oct 12 '16 at 19:46
  • @Pointy: While that's true, it's beside the point. The rub is with people saying `onclick="foo();"` bad but `ng-click="foo();"` good. They both violate this supposedly sacred *"unobtrusive JS"* principle. Heck, even adding a class to wire up a handler could be considered obtrusive... and perhaps more so since it ventures into CSS territory. –  Oct 12 '16 at 19:53
  • Right; the style opinion to me may be the intent of the question, but if so I think it's kind-of an apples-to-oranges thing. The Angular designers explicitly decided that that's the way it would work, so if you're writing Angular code you can think it's "nice" or "bad" or whatever, but you're going to either go with the flow or make a lot of extra work for yourself. The "why" is something you'd have to ask the Angular folks. – Pointy Oct 12 '16 at 20:01

4 Answers4

3

Your confusion comes from misunderstanding what's behind onclick and ng-click. Although they are both attributes, they are processed by different entities. The former is the way to add event listener to trigger callback when a click event occurs on DOM element. This attribute is processed by a browser and your callback, specified in this attribute, is executed by a browser. The latter is called angular directive in a form of an attribute, and a browser knows nothing about it. It's processed by the framework and some logic, like triggering event handler, is set up by the framework. If you don't use the framework, than ng-click is going to live there unattended, and you won't have your callback executed on click event.

Here is the relevant part of ngClick directive - the code executed by the framework when it processes ng-click attribute:

ngEventDirectives['ngClick'] = ['$parse', '$rootScope', function($parse, $rootScope) {
    return {
        restrict: 'A',
        compile: function($element, attr) {
            // fn "points" at the function you specified in the `ng-click`
            // and will be executed below when a click event occurs
            var fn = $parse(attr['ngClick'], null, true);
            return function ngEventHandler(scope, element) {
                element.on(eventName, function(event) {
                    var callback = function() {
                        // here `fn` is being executed
                        fn(scope, {$event:event});
                    };
                    if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
                        scope.$evalAsync(callback);
                    } else {
                        scope.$apply(callback);
                    }
                });
            };
        }
    };
}];

You can see, that when angular processes ng-click attribute, it executes the function ngEventHandler, which binds the custom callback to the click event of the DOM:

element.on(eventName, function(event) {

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
2

When you set an "onclick" property of a DOM element, the value remains exactly what you assign. The browser does not perform the same "convert the string to a function" operation that it does when you create an "onclick" attribute in the HTML source.

The "ng-" properties are interpreted by Angular code, and it can do whatever it wants to set up the event handler, including treating the string as the body of a function.

If you want to set up an event handler via the DOM "onclick" property, you have to assign a function, not a string:

foo.onclick = myFunction;

or

foo.onclick = function() { myFunction(someArgument); };
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    I haven't voted yet, but why is a *"convert the string to a function"* operation inferior? Or am I reading too much into your statement? –  Oct 12 '16 at 19:23
  • @squint I'm not making any value judgements here :) I interpret the OP's "OK" to mean "works" vs. "doesn't work". Assigning a string to an "ng-click" property in Angular seems pretty idiomatic, though I'm hardly an Angular expert. – Pointy Oct 12 '16 at 19:23
  • But why are so many people using `ng-click` instead of listening for a click event in JavaScript? – qwertytrewq Oct 12 '16 at 19:24
  • 1
    Fair enough, but his statement was that it's generally not OK. I'm not seeing the works/doesn't work distinction. I'm not really understanding how this answers the question. –  Oct 12 '16 at 19:25
  • @qwertytrewq because they're using Angular. If you're not using Angular, then "ng-click" is "not OK" in the sense that it won't work. – Pointy Oct 12 '16 at 19:26
  • @Pointy, despite the risk of being downvoted here, I've decided to give my own answer :) – Max Koretskyi Oct 12 '16 at 19:27
  • @squint `document.getElementById("foo").onclick="someFunction()";` does not work; the function won't be called. That's why it's "not OK". I agree however that the intended meaning of "OK" in the question is unclear; however, the distinction that one does work and one doesn't seems more important than the question of whether one or the other is considered "good style" or "bad style". – Pointy Oct 12 '16 at 19:28
  • 1
    @Pointy: I don't think that's what the OP is referring to. Pretty sure the `onclick="someFunction()"` is meant to be an HTML attribute. –  Oct 12 '16 at 19:29
  • @squint you could be right. It's an unusual question. – Pointy Oct 12 '16 at 19:44
0

The magic behind two way binding is digest cycle.

  1. What is $apply? $apply takes you from javascript context to angular context. Internally uses $digest(triggers digest cycle).

So, Javascript - $apply - digest cycle - view.

Difference between $(element).on('click', callback) and ng-click.

1.$(element).on('click', callback)

$(element).on('click', function(){
  //do your stuff
  //It is in Javascript context
  //Not known to angular
})

2.ng-click implentation

element.on(eventName, function(event) { //Here, event name is 'click'
   var callback = function() {
     fn(scope, { $event: event });
   };       
   scope.$apply(callback); //Here, ng-click wraps your code in $apply  to take you from javascript context to angular context (Triggers digest cycle).
});
ram1993
  • 979
  • 1
  • 9
  • 13
-2
$("#my-button").on('click', myFunction)

is a code in JavaScript, using jQuery.

ng-click = "myFunction()"

is using AngularJS's ability to read through the attributes of the tags.

onclick = "myFunction();"

is the "old", HTML way to call a function.

PiotrK
  • 1,502
  • 1
  • 15
  • 33