Angular and Jade sitting in a tree
or how to reduce Angular forms boilerplate
Writing AngularJS apps has always been a pretty enjoyable experience in my case. Thanks to its declarative nature you can build a lot of functionality not having to type all that much code. Directives ftw and all that.
In fact, the only exceptions to the rule are forms.
And these are the painful ones. The amount of boilerplate code you have to write is depressing. You start with a standard, proper html5 stuff: your ids, your label fors, your types and names. Then you add the Angular layer: ng-model, validation directives, and last, but certainly not least, the error messages copy.
Let’s see a typical sample:
For those not so familiar with AngularJS I would like to stress that the code above is responsible for displaying a single field. 10 lines for a single not-so-fancy field and some error messages.
Now, what we see here is a lot of repeating ourselves. Just look at line #5. Hell, look at all the lines and count the `username` occurances! And the value for `ng-show=` is such a boilerplate, I don’t believe anyone would write it by hand next time.
Well, you could make a directive to encapsulate this part for later reuse, but I don’t think creating directives to generate directives (and exchanging our code management problem to browser’s lower performance in the process) is the best idea…
Enter the jade
What is jade? A tiny introduction: “Jade is a terse language for writing HTML templates.” Okaay, so what does it look like? Here’s our little form written in jade:
The syntax is simple: a string at the beginning of the line will be transformed to a tag name, if it’s preceded by a dot it will be a div with of given class, and # sign corresponds to the id attribute. You can also combine it — like in line #4 of the example. If you’ve written any CSS in your life, it should feel familiar, we’re looking at basically the same rules as simple selectors. The rest of html attributes are added in parenthesis.
Other than that, you’ll notice that there are no closing tags here. Yes, jade is one of those languages where indentation does matter. You can love it or you can hate it, I think that in case of regular html you most likely still format it in similar fasion so we can as well make it mandatory. There’s no need to allow any artictic freedom on this matter.
What we’ve gained is 3 lines shaved off of the original markup’s length.
It isn’t all that exciting though: yes, we have a slightly smaller codebase, but does it justify adding an extra tool in our project? Well… maybe. But let’s say we are not convinced yet. Remember, we still have all those username strings all over the place. If it was some kind of algorithm and we’d simply store it in a variable, wouldn’t we? We would probably also realize that this form field pattern will be quite repetitive throughout our forms and try to put it into a function to DRY our code. Thankfully…
Jade has mixins
You can think of mixin in jade you as a function: it has a name and optional arguments. It also returns a jade markup. You can read all about it in the docs, in the meantime let’s look at how it can be used in our case:
Whoa, what now? A lot of new stuff here. E.g. inside of a mixin we have access to the attributes object, which holds the values of all attributes defined on the mixin. Ok, so now let’s break the code up line by line:
- #1 — we define a field-text mixin. It takes a single argument: label
- #2 — it will output a label tag with for attribute’s value set to id attribute if we provide it. Otherwise it will set it to name attribute’s value.
Then it will print (hence the “=” sign) the value of the label argument. - #3 — it will output the input element of class form-control with the id attribute’s value corresponding to the label’s for. And set type to text.
The &attributes(attributes) construct means that it will also have all the others attributes defined when using the mixin (think ng-model, required, etc.)
The second mixin introduces one more concept: a block. You can think of it as a Angular’s transclude: it will be replaced with the markup put inside a used mixin. In our case we’ll want to define specific error messages there.
Ok, we’ve come so far, it’s time to put those mixins to work. Here’s our form after the refactoring:
I’ve extracted the mixins to a separate file, so I can use(include) it in other forms.
Epilogue
Now, what we were working on was only one text field. Imagine a more complicated form: several fields, each still with it’s own set of classes, ids, names, labels, fors… How much code will you save? How many potential typos? Also, though for the first several hours of working with jade you’d probably won’t see it that way — as you were looking at html code for years — but it really is more readable. You’ll get used to it, trust me.
Oh, and one more thing regarding the jade compilation step: it does add one more task for your grunt/gulp/etc but in return it will also not pass any syntax error through. So while you could leave a “<” or “/” somewhere in your html and (hopefully) realize only when inspecting the app later in your browser, the jade compiler will hit you with nice big error if you try to feed it with something stupid.
So what do you think? Do you have any questions about the provided example or implementation in a real-world project?