Replacing Jade with Hogan in an Express.js App – the MEAN stack

Previously, I described and diagramed the MEAN stack, this time I take on templates.

I recall someone counted up to 26 major javascript templating engines of various ideologies, syntaxes and flavors.  This is madness and it means it is very likely a pre-existing boilerplate or project you’ll be working with will have a templating engine which you aren’t used to, or simply don’t like. With me, both apply to Jade. Call me old-fashioned (despite still being in my 20s, that’s how crazy the front-end world pace is), but I’m neither used to nor like my HTML or Javascript to change their syntax and to be white space sensitive. While it does save you symbols, there is something beautiful about actual code in a slightly twisted way. I’d rather use snippets in Sublime Text and Emmet. So when I got the MEAN stack I immediately wanted to switch to Hogan.js which is basically Twitter’s version of Mustache for Node.js. Using Hogan is also natural in the MEAN stack, because Angular uses the same Mustache syntax and you will in effect be using the same type of templates fort both server-side and client-side views.

Express comes with the option upon generation of a fresh application to use Hogan by typing:

express --hogan

What this amounts to is adding Hogan as a dependency in package.json, generating an index.hjs file and replacing a line in your configuration in express.js from:

app.set('view engine', 'jade')

to

app.set('view engine', 'hjs');

But to my understanding this does not support partials aka includes and if you look closely at the Jade in the MEAN stack, it uses include, extend and block tags, all of which are ways to include content from one file in another which is very useful, because it allows you to make default layouts, headers, footers etc. without having to repeat the code all over. Pretty much every good server-side language has this.

Luckily there is a solution for this within Express that is found here https://github.com/vol4ok/hogan-express
But as a Node user, you can simply use NPM to install it by adding it to the dependencies package.json file in the project’s root directory, li

...
"dependencies": {
     "express": "latest",
-    "jade": "latest",
+    "hogan-express": "*",
     "mongoose": "latest",
     "connect-mongo": "latest",
     "connect-flash": "latest",
...

and running

npm install

After your project has installed the new hogan-express dependency, you are ready to set it as an app engine, and since it uses actual html files, you remove the setting of jade as a view engine and set html as a view engine like so:

app.engine('html', require('hogan-express'));
app.set('view engine', 'html');

If you test now, you should be able to open in the browser your index.html files which can also include mustache logic in them.

So far so good, but the MEAN stack had used a fair amount of Jade which needed replacing and Jade looks like this:

!!! 5
html(lang='en', xmlns='http://www.w3.org/1999/xhtml', xmlns:fb='https://www.facebook.com/2008/fbml', itemscope='itemscope', itemtype='http://schema.org/Product')
  include ../includes/head
  body
    header.navbar.navbar-fixed-top.navbar-inverse(data-ng-include="'views/header.html'")
    section.content
      section.container
        block content
    include ../includes/foot

This is a problem, because you can’t just copy the HTML and replace the interpolation of the template, you actually would need to rewrite the whole thing. So I went to Chrome Dev Tools and copied the output of real HTML.

After that I went over all the Jade templates and looked for insertions of logic and dynamic data along with includes, extends and blocks which I replaced all with the Mustache syntax. I liked  the way Jade had been used to create a default layout and I also found that hogan-express can be used to set a global default layout and partial insert which is accomplished by adding the following 2 lines to express.js

app.set('layout', 'layouts/default');
app.set('partials', {header: "includes/header"});

The first line will wrap all your further hogan views within this express with this default layout file, the actual view content will fall wherever you put the

{{{ yield }}}

clause in the layouts/default.html code. The second line will make the header partial available for inclusion in any view whenever fit with the following syntax:

{{> header}}

That’s almost it, you are at this point able to pass dynamic data from the Express controller to the HTML view like this:

exports.show = function (req, res) {
  var user = req.profile
  res.render('users/show', {
    title: user.name,
    user: user
  })
}

And render it in the view with the Mustache syntax:

Hello, {{user}}, the title is {{title}}.

The final point to make is that if you want to bootstrap json data from the Express controller to the javascript in the view, which is a very good idea and is used by the MEAN stack, you need to be careful, because Mustache normally will escape the quotes, but this would ruin the JSON. You need to use the triple mustache syntax:

 window.user = {{{ user }}};

This will make sure your JSON is intact and you can use it as data on the client-side of your app.

You can review the whole application here.

8 thoughts on “Replacing Jade with Hogan in an Express.js App – the MEAN stack

  1. Permalink  ⋅ Reply

    karl

    September 17, 2013 at 6:10pm

    This was extremely helpful, thanks for the detailed post and MEAN!

  2. Permalink  ⋅ Reply

    Ryan

    November 12, 2013 at 11:16am

    Where does req.profile come from in the show function of the user controller? The profile attribute seems to be produced out of thin air.

  3. Permalink  ⋅ Reply

    Sahat

    December 9, 2013 at 8:18am

    Thanks for a great writeup. Up until now I have used Jade primarily due to template inheritance using extend/block. Handlebars syntax is what I really wanted to use all along. Will give Hogan.js a try.

  4. Permalink  ⋅ Reply

    David Higginbotham

    January 7, 2014 at 1:27am

    I love Jade, or alternatively any template engine that doesn’t listen to {{}} so I can share views with my client app and my server side views, also allows me an added bonus of added security with authentication happening on client side view requests. I like hogan though, it’s very easy to switch it up so you can share the two. Good writeup 😉

  5. Permalink  ⋅ Reply

    Ben Drury

    June 17, 2014 at 6:37am

    Hi. This is great and really helpful. Thanks.

    I am getting a run error on it though that I’m struggling to track down. I presume it’s to do with packages no longer being bundled, but can figure out which one. Hoping you can help.


    Error: Most middleware (like session) is no longer bundled with Express and must
    be installed separately. Please see https://github.com/senchalabs/connect#middl
    eware.
    at Function.Object.defineProperty.get (D:\Dropbox\Controlled_Projects\MEAN-w
    ith-Hogan\node_modules\express\lib\express.js:89:13)
    at module.exports (D:\Dropbox\Controlled_Projects\MEAN-with-Hogan\node_modul
    es\connect-mongo\lib\connect-mongo.js:30:39)
    at Object. (D:\Dropbox\Controlled_Projects\MEAN-with-Hogan\config
    \express.js:6:42)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object. (D:\Dropbox\Controlled_Projects\MEAN-with-Hogan\server
    .js:43:1)

    • Permalink  ⋅ Reply

      sparkyfied

      September 26, 2014 at 12:51pm

      Head over to this page https://github.com/senchalabs/connect#middl
      eware and have a look at the middleware section. The module you are using that is missing is ‘session’: “Error: Most middleware (like session)”. You should be able to look in the list they have provided for the corresponding module name and install it using npm.

  6. Permalink  ⋅ Reply

    Juni Brosas

    April 28, 2015 at 10:15am

    Thanks for this tutorial. It works for me (Y)

Leave a Reply

Your email will not be published. Name and Email fields are required.