Whiskered Node views

As we draw closer to the glorious month of Movember, I find myself pondering the myriad template engines available for Node apps. The most popular is still probably Jade as its syntax is Haml-like and results in quite clean views, lacking in HTMLish clutter.

While Jade is handy, it takes some time to get used to. Plus, if you find yourself working with a UI person who prefers to speak in HTML, you’ll find yourself translating between HTML and Jade (which isn’t that hard with web apps like HTML2Jade, but nevertheless involves an extra translation step).

There are other template engines that map more closely to pure HTML. Mustache, for instance, forgoes reducing HTML entirely and introduces {}’s (i.e. mustaches) as a substitution delimiter. Thus, you can take normal HTML files and add some {}’s to make pages dynamic.

In the world of Node, there are a few Mustache implementations. One of the more interesting ones that I’ve used is Whiskers. Whiskers is fairly lightweight and doesn’t offer a lot of bells and whistles. As the project’s README states

Whiskers is focused on template readability. By limiting template logic, careful preparation of the context is encouraged, and the processing and formatting of data is kept separate from the design of the display.

Accordingly, you can do variable substitution, conditional logic, and looping out-of-the-box easily. But that’s about all.

To get started with Whiskers, you’ll need to add it as a dependency to your project’s NPM file like so:

package.json NPM file
<span class='line-number'>1</span>
<code class='json'><span class='line'><span class="s2">"whiskers"</span> <span class="err">:</span> <span class="s2">"latest"</span>

In this case, I’ll always be grabbing the latest version.

I prefer CoffeeScript when writing Node apps; consequently, the code examples I show you will be in CoffeeScript. Accordingly, in my App.coffee file, I need to then require Whiskers:

Requiring whiskers in your Node app
<span class='line-number'>1</span>
<code class='coffeescript'><span class='line'><span class="nv">whiskers = </span><span class="nx">require</span> <span class="s">'whiskers'</span>

You’ll need to configure Express to leverage Whiskers; luckily, Express makes plugging in alternate template engines quite easy.

Configuring a template engine with Express
<span class='line-number'>1</span>
<span class='line-number'>2</span>
<code class='coffeescript'><span class='line'><span class="nx">app</span><span class="p">.</span><span class="nx">set</span> <span class="s">'view engine'</span><span class="p">,</span> <span class="s">'html'</span>
</span><span class='line'><span class="nx">app</span><span class="p">.</span><span class="nx">engine</span> <span class="s">'html'</span><span class="p">,</span> <span class="nx">whiskers</span><span class="p">.</span><span class="nx">__express</span>

This indicates that your template files will end in .html and that for those file types, use the Whiskers framework.

You can then render a Whiskers template like normal. For example, if I want to pass an allWords collection as the variable words to a template file dubbed index.html, I can do it like so:

Rending a view
<span class='line-number'>1</span>
<code class='coffeescript'><span class='line'><span class="nx">res</span><span class="p">.</span><span class="nx">render</span> <span class="s">'index'</span><span class="p">,</span> <span class="p">{</span><span class="nv">words: </span><span class="nx">allWords</span><span class="p">}</span>

In this case, allWords is an array full of Word classes.

Inside my index.html file, I can access the words variable inside a bracketed for loop like so:

Mustached HTML
<span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<code class='html'><span class='line'><span class="nt"><body></span>
</span><span class='line'>  {for word in words}
</span><span class='line'>    <span class="nt"><div</span> <span class="na">data-role=</span><span class="s">"page"</span> <span class="na">id=</span><span class="s">"page_{word.id}"</span> <span class="na">data-theme=</span><span class="s">'c'</span><span class="nt">></span>
</span><span class='line'>      <span class="nt"><div</span> <span class="na">data-theme=</span><span class="s">"g"</span> <span class="na">data-role=</span><span class="s">"header"</span><span class="nt">></span>
</span><span class='line'>        <span class="nt"><h3></span>
</span><span class='line'>            Overheard Word
</span><span class='line'>        <span class="nt"></h3></span>
</span><span class='line'>      <span class="nt"></div></span>
</span><span class='line'>
</span><span class='line'>      <span class="nt"><div</span> <span class="na">data-role=</span><span class="s">"content"</span><span class="nt">></span>
</span><span class='line'>        <span class="nt"><div</span> <span class="na">class=</span><span class="s">"center-wrapper"</span><span class="nt">></span>
</span><span class='line'>           <span class="nt"><h2></span>{word.spelling} <span class="nt"></h2></span>
</span><span class='line'>           <span class="nt"><p><em></span>{word.partOfSpeech}<span class="nt"></em></span> - {word.definition}<span class="nt"></p></span>
</span><span class='line'>           <span class="nt"><p></span>"{word.exampleSentence}"<span class="nt"></p></span>
</span><span class='line'>         <span class="nt"></div></span>
</span><span class='line'>      <span class="nt"></div></span>
</span><span class='line'>    <span class="nt"></div></span>
</span><span class='line'>  {/for}
</span><span class='line'><span class="nt"></body></span>

Note inside the for loop, I have access to a word instance. I can call properties on it as well. Note, with Whiskers, you can’t invoke methods on passed in objects. Only properties (i.e. word.definition isn’t a function).

Jade certainly produces more elegant, less verbose view code. But Jade’s whitespace delimiting coupled with the fact that basic HTML knowledge is near universal, make template frameworks like Whiskers, which permit normal HTML with {} delimiters appealing from time to time.

This story, "Whiskered Node views" was originally published by JavaWorld.


Copyright © 2013 IDG Communications, Inc.