Angular.js is a powerful clientside JS framework that aims to enhance HTML for web applications. I think it already an excellent and feature-complete framework and it is only going to get better. But it is a new framework, and its documentation is currently in a state of flux, often riddled with outdated references to beta versions, and sometimes just damn confusing.
I struggled with understanding how to make a directive and after too many hours, I succeeded. I am writing up my thoughts on the matter here for my future self, and all who happen upon this gist.
In honor of the effort it took to learn how to build a proper directive in Angular, I will be creating a widget that I call a swear jar — an HTML element that may be included as any other element would — i.e., by writing <swear-jar></swear-jar>
— and that, when clicked by a profane programmer, adds a dollar to its contents, displays how much money is in the jar, and in the process immediately absolves the programmer of his profanity, if not his misanthropy.
The great thing about angular.js (AJS) is that it allows you to naturally extend HTML syntax to build components that may be reused throughout your application. The principal method for doing this is the directive, as indicated above, but before we delve into directives, which are not so hairy in the end, let's review the basics of angular.js. Much more on this may be found on the angular.js site. I particularly recommend the excellent tutorial.
Let us begin with an empty stretch of HTML. I assume we've loaded in the angular.js file, set our DOCTYPE, and all the rest. To see what I actually mean, please check the JSFiddles that accompany this text. If a picture is worth a thousand words, running code must be — from my back of the envelope calculation — worth at least 10^15 words, which you must agree is a large number. What I'm saying is this: check out the JSFiddle examples.
The code discussed immediately below lives at Snippet #1, for example.
<div ng-app>
<p>The sum of two integers: {{2+3}}</p>
</div>
The magic here is the inclusion of ng-app
attribute in the opening <div>
. This attribute (which really corresponds to a directive — more on this later) signals the AJS HTML compiler to run through the contents of the <div>
and do its thing. Its thing, in this case, is to encounter the expression, {{2+3}}, and evaluate it to produce 5
. You can think of the contents of those double braces as any sort of valid JavaScript expression, with a few but notable exceptions.
One such exception is that the contents of those double braced expressions are evaluated not against the global window, as would be standard JavaScript expressions, but against a specified scope. Scope is sort of a BFD in Angular, which claims to be lethally allergic to all things global.
How do we provide scope? A controller will do the trick. We can make one pretty easily (Snippet #2). We first gently modify our HTML,
<div ng-app ng-controller='MyController'>
<p>My nickname is {{nickname}}!</p>
</div>
And then build a controller in JavaScript,
var MyController = function($scope) {
$scope.nickname = 'Aristotle'
}
When the faerie dust settles, we see: My nickname is Aristotle!
A couple of things to note here. In our HTML, in addition to the ng-app
attribute, we have now also specified ng-controller='MyController'
. This indicates that the scope of this element and its children will be, well, controlled by the MyController
controller. This name matches the name of the JavaScript function we defined above, and this is not a coincidence.
Notice that the MyController
function takes as input the $scope
object. This is an example of Angular's delightful reliance on dependency injection, a full discussion of which is beyond the $scope
(heh, see what I did there?) of this article.
Assigning an attribute to the $scope
defines a model. Or conversely, a model is any data that is reachable from a property on the $scope
object. These model attributes are available to the view — basically, that snippet of HTML — and can be accessed using those double braces and other tricks.
When we attach a property called nickname
to the $scope
object, it becomes available to expressions in our HTML. This behavior alone might make Angular compelling, but its real power lies in its ability to extend HTML itself, and for this it employs the directive.
As the Angular folks put it, directives are a way to teach HTML new tricks.
Sadly, before we can talk about directives, we must briefly detour to discuss modules
.