Jade Mixins & Includes
The latest release of Jade 0.13.0 adds mixins and includes. Jade users have longed for some method of static include, so it’s (finally) here, and compliments mixins nicely, allowing you to store mixins in one or more separate files.
Mixins
A mixin definition takes the form mixin <name> [( params )] block, where params is identical to JavaScript function params, simply a list, as it’s converted to a JavaScript function within Jade to become part of the output function.
Using a mixin is identical to the definition, however omitting the block. Mixins allow you to encapsulate repeated chunks of a template, such as form fields as shown in the following snippet. This example has a local variable user passed to the template with the properties { name: '...', email: '...' }, which is then accessible throughout any mixins defined without explicitly passing it (though you may if you like).
mixin field(type, name, label)
.field(class='field-' + type)
label #{label}:
input(type=type, name='user[#{name}]', value=user[name])
form
mixin field('text', 'name', 'Username')
mixin field('text', 'email', 'Email')
mixin field('password', 'pass', 'Password')
input(type='submit', value='Sign Up')
Outputting:
<form>
<div class="field field-text">
<label>Username:</label>
<input type="text" name="user[name]" value="TJ Holowaychuk"/>
</div>
<div class="field field-text">
<label>Email:</label>
<input type="text" name="user[email]" value="tj@learnboost.com"/>
</div>
<div class="field field-password">
<label>Password:</label>
<input type="password" name="user[pass]"/>
</div>
<input type="submit" value="Sign Up"/>
</form>
Includes
The include <path> directive signals Jade to read and parse the file, then return it’s root node (a block), injecting it into the calling template, as if it were written in the same file. The <path> given is relative to the dirname of the calling template, which is exposed to Jade via the filename option, which is populated by jade.renderFile() and Express, otherwise you need to supply this.
For example our mixin(s) could live in ./mixins, and our previous example could look something like below:
include mixins/form-helpers
form
mixin field('text', 'name', 'Username')
mixin field('text', 'email', 'Email')
mixin field('password', 'pass', 'Password')
input(type='submit', value='Sign Up')
Or the classic header / footer example, first index.jade:
html
include includes/head
body
h1 My Site
p Welcome to my super lame site.
include includes/foot
includes/foot.jade:
#footer
p Copyright (c) foobar
includes/head.jade:
head
title My Site
script(src='/javascripts/jquery.js')
script(src='/javascripts/app.js')
That’s all for now, hopefully I’ll have some time in the near future to clean things up and get 1.0 (finally again :)) out the door.
Jade Templates Introspection
The Jade Template Engine for Node now have the ability to manipulate themselves based on the parse tree constructed from a nested block. This has extreme potential, in that developers can now alter jade templates dynamically, constructing their own syntactic sugar.
One of my favorite (but not only) use-cases for this functionality, would be that of a database record edit form. Typically your template may be littered with helper functions such as input(‘text’, user.name) or similar. With the ability for filters to now accept a block of tags, we can apply the Visitor pattern in order to convert tags to other tags, alter existing tags or manipulate Jade in almost any way without writing an entirely new compiler.
So how is something like an edit form typically handled? With rails / ERB it might look something like this, which although is “fine” it is generally in my opinion difficult to read, and clearly mixes code with the template.
Although Jade provides an arguably cleaner syntax, it does still essentially have the same issue of constantly mixing verbose logic within the template itself to do common things like display errors on a field as shown below.
With Jade’s new reflection capabilities we could vastly simplify these patterns by creating a :model filter and manipulating the parse tree. Below we lay out how we would prefer to handle model forms, we could take this even further and check model property types to attempt creation of the appropriate field types automagically, but for simplicity we will not.
Everything you see below is valid jade, however we utilize attributes to simulate argument passing, which is fantastic because we could easily transparently support input error handling without the use of our custom field tag.
The output we will achieve after our parse tree manipulation will be:
And here is our filter / Visitor implementation. Although the DOM-like node api is verbose, this extra layer adds endless flexibility to Jade, and while you are at it, why not generate the elements constructed below with Jade as well ;) using Jade’s new jade.parse(str) method.
Keep in mind that all of this is performed at the compilation level, typically which is only called once per template, so essentially no overhead over the lifetime of an application. That is it for now, make sure not to abuse this functionality :p.
happy Jade-ing?
Jade Screencast - Template Engine For NodeJS
Introduction
This screencast covers an introduction to the Jade Template Engine for Node. Jade is an indentation based template engine inspired by Haml, with added support for the popular Express web development framework.
Iteration, Conditionals, and Debugging
Learn how to iterate objects and arrays, template conditionals, the debug option and how Jade’s error reporting works in this screencast.
Jade - Haml killer for nodejs
Last night I open sourced my latest project Jade, a template engine for node, focusing on readability, error handling and performance. Jade spawned from several needs, first I was tired of debugging poor JavaScript template implementations, or finding work-arounds for common issues, secondly I love haml’s syntax, however I feel it could be revamped to provide a more enjoyable experience, and lastly … I like parsers!
Examples
So what does this so called “Jade” look like? like this:
!!! 5
html(lang="en")
head
title= pageTitle
:javascript
| if (foo) {
| bar()
| }
body
h1 Jade - node template engine
#container
- if (youAreUsingJade)
p You are amazing
- else
p Get on it!
If you are at all familiar with Haml you will see tons of similarities,
however with a few important changes. First of all inverting the responsibility of whitespace we can remove the % tag prefix, leading words are now simply tags, and text blocks now contain a margin as indicated by the pipe |. I am sure people will have varying opinions on this, but I find it much easier to read, and more enjoyable to type since your main focus is building a layout, not writing text blocks.
Features
Below are some of the highlights of 0.0.1:
- high performance parser
- great readability
- code is escaped by default for security
- contextual error reporting at compile & run time
- executable for compiling jade templates via the command line
- html 5 mode (using the !!! 5 doctype)
- optional memory caching
- combine dynamic and static tag classes
- no tag prefix
- filters
- :sass
- :markdown
- :cdata
- :javascript
Implementations
As of this moment JavaScript (node specifically) is the only Jade implementation, however as with Haml this could easily be ported to other host languages, and I encourage it greatly! Even if you are not a JavaScript guy feel free to try it out, and implement it in your language of choice :)
More Information
Head over to the Github repo, or visit the Jade site for additional examples, installation guides and more.