If Angular directives are obtrusive, then so is an "id" attribute.

I wanted to clear some things up about AngularJS directives. I keep seeing comments like this one:

I will admit that Google hasn't done the greatest job with Angular's documentation and reading it seems to make Angular feel more complicated rather than less. So I do understand why there is a good amount of confusion around some of these things. However, don't throw the baby out with the bathwater just yet.
There is a huge difference between old-school attribute event handlers, such as the one in the comment screen shot above, and Angular directives. The following two pages from AngularJS by Brad Green and Shyam Seshadri provide a great explanation for why Angular directives are not obtrusive.


After sharing this information with a few people who continue to chant this criticism, I had a couple of them tell me that directives are still obtrusive and are an example of "logic in the presentation" (which is a big no no on the modern web).
What a lot of developers don't seem to grasp is that a directive is harmless as well as useless without the presence of the Angular library to look for it and do stuff with it. All you're doing is making your markup more semantic and then letting a library use those semantics to do things for you that would otherwise require a little more ground work to accomplish.
If you still think angular direcives are obtrusive then I ask you this: What is the purpose of an "id" attribute? Is there any default functionality that occurs when you give an element an "id" attribute? Technically yes, because even in 2014 browsers still think it's cool to create global JavaScript variables for every "id" attribute in your DOM (don't get any ideas, stick to getElementById
). Other than that little quirk though, is there anything worth noting that happens just by you giving an element an ID?
The primary purpose of an "id" attribute is to paint a target on an element so you can easily get reference to it in your code. Think of an ID like a beacon in the markup making it easy for you to navigate the otherwise potentially messy DOM. Is an ID logic in the presentation? As long as you don't count that global variable nonsense that browsers still do, then of course not. An ID by itself is pretty harmless, and without the code that is hunting for that ID it's also pretty useless.
The same thing can be said about an Angular directive. The difference is that a directive can convey a lot more information to the code when it gets around to parsing the portion of the DOM.
<script>
$(document).ready(function () {
var $myButton = $('#myButton');
$myButton.click(function () {
alert('Did stuff.');
});
});
</script>
<button id="myButton">Click Me</button>
In the above code we throw an ID onto our button so we can get reference to it with JQuery. Then in our JQuery code we do several things manually. First, we wait for the document to be ready before we do anything. Most of the time we tend to throw script
tags in the head
of our page so we need to wait for the DOM to be fully loaded and ready before we try to access it.
Second, we get reference to our button element using JQuery's syntax. We store that reference as a variable in case we need to use the button again later. Littering our code with calls to $()
can be expensive (in later versions of JQuery it stores these references behind the scenes for you so the performance hit is greatly mitigated).
Lastly we assign our button a click event and an event handler, in which we call an alert and display a message. That was a lot of work just to get to this point. Even the simple things, like waiting for the DOM to be ready, are easily forgotten by experienced developers. Compare that to the same thing in Angular.
<script>
function myController($scope) {
$scope.doStuff = alert.bind(window, 'Did stuff.');
}
</script>
<button ng-click="doStuff()">Click Me</button>
Believe it or not, that's not javascript inside the ng-click
attribute. It's Angular expression syntax, which describes what logic should take place when Angular parses that particular element. We can already see how much additional information this attribute applies. Seeing an ID on an element is meaningless most of the time; if stuff happens when you interact with that element then you have to go hunt through code to find reference to that ID before you can even see what's going on.
An Angular directive provides more semantic meaning to the markup it's attached to. We can easily see that the attribute is notifying Angular that it should create a click event handler and that the handler should map to a doStuff
function on our scope. We can even obtain this information simply by right-clicking the element in the page and clicking "Inspect Element"; talk about making debugging so much easier.
An "id" attribute and an "ng-click" attribute are barely different. The only difference being that the latter describes a small portion of the boilerplate behavior that Angular should perform when it gets there. Notice that in Angular we never had to wait for the DOM to be ready, nor did we have to manually create a click event handler. Angular is smart enough to take care of that tediousness for us. We merely had to write the function that we wanted to call and supply the attribute.
Well then all code merely "describes" logic, right?
Believe it or not I've actually had someone say this to me. If Angular expression syntax merely describes logic to be performed then every programming language ever written merely describes logic. That's true, but totally not the point I was trying to make. I am responding to this odd point explicitly because I've heard it more than once. When we say that Angular's expression syntax describes logic to be performed, we mean that without Angular on the page, nothing breaks. There is no logic being performed simply by virtue of adding the directive to the page. None.
<script>
function doStuff() {
alert('Did stuff.');
}
</script>
<button onclick="doStuff()">Click Me</button>
The above code does not merely describe logic. When you use the old-school event handler attributes you are actively telling the browser to perform logic on that element. This ties the logic directly to that markup. In other words, without the DOM present the code will not function and is very difficult to test.
In Angular, you can remove the DOM entirely and still test all of your code in a simple and clean way. You can't even do that with JQuery. Without the DOM present JQuery can't hunt for the elements it needs, thus it becomes impossible to test.
$(function () {
$.ajax({
url: '/getAPage',
success: function (html) {
$('#viewContainer').fadeOut().html(html).fadeIn();
}
});
});
In the above code we make a call to the server to get some markup and then we place that markup in the DOM where it belongs. The code directly talks to the DOM and even describes the animation behavior it should exhibit when displaying the new markup. Testing this code without the DOM would be a difficult task indeed.
Not only are directives unobtrusive JavaScript, they are less obtrusive than JQuery and "id" attributes.