Angular's use of Inline JavaScript in HTML attributes is not "bad practice"? [closed]

As I read through the Angular tutorials, I really like a lot of it, but isn't "ng-click" the equivalent of an inline onClick? My understanding was that the JavaScript community had determined inline JavaScript event handlers in your HTML was "bad practice."

<img ng-src="{{img}}" ng-click="setImage(img)">

It would be great to know why this is no longer considered "incorrect" when using Angular.

Source: http://docs.angularjs.org/tutorial/step_10


Really, it all comes down to the fact that your view code has to be hooked into your application logic somehow. Best practices for AngularJS generally state that you should write your own models--objects that represent your business domain--and attach them to the scope. Imagine some code like this:

<img ng-src="{{img}}" ng-click="myProfile.setMainImage(img)">
myApp.controller("ProfileController", function($scope, myProfile) {
  $scope.myProfile = myProfile;
});

The view says "when this image is clicked, it will call setMainImage() on myProfile." The business logic is inside myProfile, where it can be tested, etc. The view is just a hook.

In a more "traditional" or "vanilla" jQuery setup, you'd have to write something like the following:

$("#someId img").on('click', function() {
  var img = $(this).attr('src');
  myProfile.setMainImage(img); // where does myProfile come from here?
                               // how does it update the view?
});

Of course, the JavaScript community has determined that writing large applications in this fashion isn't really tenable, in part because of the disconnect between the views and the model objects (as indicated by the comments in the code snippet), which is why we have frameworks like Angular in the first place.

So, we know this native jQuery code is not ideal, but we're still not sure about the whole ngClick thing. Let's compare it to another very popular JavaScript framework that provides an MV* architecture, Backbone. In a recent RailsCasts episode on AngularJS, someone asked a very similar question:

Is it just me, or AngularJS looks so a bad idea? Ryan, don't get me wrong, the episode was great, but I'm not convinced by the framework.

All that ng-show, ng-repeat, ng-class are looking like the old Java's JSF, and similar frameworks. It also enforces obtrusive JS with ng-submit and ng-click.

So my point is: your view will easily become cluttered and totally dependent on it. The advantage of other frameworks like Backbone, is to have a separation of concerns between the presentation and the behavior (less or no dependencies), and a structured client side application (MVVM).

My response is applicable here as well:

In a framework like Backbone, you'd have something like the following code (taken from the Backbone website, minus a few lines):

var DocumentView = Backbone.View.extend({

  events: {
    "dblclick"                : "open",
    "click .icon.doc"         : "select",
    "contextmenu .icon.doc"   : "showMenu",
    "click .show_notes"       : "toggleNotes",
    "click .title .lock"      : "editAccessLevel",
    "mouseover .title .date"  : "showTooltip"
  },

  open: function() {
    window.open(this.model.get("viewer_url"));
  },

  select: function() {
    this.model.set({selected: true});
  },

});

In this object which is a view, you are setting up event handlers on various elements. These event handlers call functions on the view object, which delegate to models. You also set up callbacks on various model events (such as change) which in turn call functions on the view object to update the view accordingly.

In Angular, the DOM is your view. When using ng-click, ng-submit, etc., you are setting up event handlers on these elements, which call functions that should delegate to model objects. When using ng-show, ng-repeat, etc. you are setting up callbacks on model events that change the view.

The fact that AngularJS sets up these [hooks and] callbacks behind the scenes for you is irrelevant; the only difference between this and something like Backbone is that Angular lets you write your view declaratively--you describe what your view is--rather than imperatively--describing what your view does.

So, in the end, <a ng-click="model.set({selected: true})"> really adds no more dependencies than

events: {
  'click a': 'select'
},

select: function() {
  this.model.set({selected: true});
}

...but it sure is a hell of a lot less code. ;)

(Note: really, the Angular version should be <a ng-click="select()">, and the select method on the scope would be like the select method in the view in the Backbone example.)

Now, perhaps a legitimate concern is that you don't like the event hooks in your markup. Personally, I greatly prefer the declarative nature of Angular's views, where your markup describes what the view is and you have two way binding between events (whether they be user generated or simply changes in the model) and your views--I find I write far less boilerplate code to hook up events (especially changes in the view driven by changes in the models), and I think it's easier to reason about the view in general.