Maintaining front end templates with handlebars.js

Until recently I had only worked on projects where I had full control of the full stack, from database to CSS. That's not maintainable and also not reflective of the real world. In this post I'll be showing how I split out responsibility for styling and templating so a front end developer can work on the same styles I am using in my project, without too many sync issues.

To follow along, grab yourself a clone of my repo at https://github.com/timcodesdotnet/handlebars.git

First off, notice the templates folder in wwwroot (it's hidden from the solution so you may need to select Show All Files first). Inside there, you have a number of HTML templates, partials folder and a dist output containing our compiled HTML, which can be viewed in the browser. Front end developers don't event need Visual Studio to edit these files, they are just standard handlebars templates with a gulp task to compile them. Let me also say that if your front end developers are comfortable with using Visual Studio and editing directly in the .cshtml views, that would obviously be preferable.

Handlebars

Handlebars.js is a front-end templating solution similar to libraries like knockout.js, angular etc. It's going to allow us to split our pages into partial HTML chunks so we can share parts of the page between templates. It's going to allow us to mimic the structure of our .cshtml views.

Have a look at the sample template for the home page:

home.page.hbs //HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Home</title>

    {{>meta}}
</head>
<body>
    {{>header title='Home'}}
     
    <h2>Welcome to my homepage</h2>

    {{>footer}}
</body>
</html>

You can probably guess that the {{>meta}} syntax is injecting a partial view into the page. The path it finds this partial at is defined in our gulp configuration, which I'll show you later. For now, take a look at the partial view it references:

meta.hbs //HTML
<link rel="stylesheet" href="../../lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="../../css/site.css" />

These are the exact same stylesheets the cshtml layout is using, so changes here are going to apply to both our templates and our main project.

Also notice how you can pass data down to your partials with parameters. This is just one way you can use handlebars, check further reading for more.

home.page.hbs //HTML
{{>header title='Home'}}

Gulp

The project makes use of gulp. If you haven't heard of gulp, it's basically a task runner written in JavaScript. It's going to allow us to compile our templates. Check the further reading section for more info on gulp.

To use gulp and handlebars, you will need the following devDependencies in your npm package.json confiuguration file. If you don't already have one, you can add one to the root of your project using the Add Item wizard and search for npm (it's a package manager for Node.js, similar to NuGet). 

package.json //JavaScript
{
	"version": "1.0.0",
	"name": "handlebars-sample",
	"private": true,
    "devDependencies": {
      "gulp": "3.9.1",
      "gulp-handlebars-html": "0.0.2",
      "gulp-compile-handlebars": "0.6.1",
      "gulp-regex-rename": "0.1.0",
      "gulp-replace": "0.5.4",
      "handlebars": "4.0.6"
    }
}

Once these packages have installed (just saving the file is enough to trigger the install), we can set up our gulp task to do the compilation:

gulpfile.js //JavaScript
var gulp = require('gulp');
var handlebars = require('handlebars');
var config = require('./gulp.config')();
var gulpHandlebars = require('gulp-handlebars-html')(handlebars);
var regexRename = require('gulp-regex-rename');
var replace = require('gulp-replace');

gulp.task('compileHtml', function () {
    var templateData = {

    },
    options = {
        partialsDirectory: [config.templatePartialPath]
    };

    return gulp.src(config.templatePath + "*.page.hbs")
               .pipe(gulpHandlebars(templateData, options))
               .pipe(regexRename(/\.page\.hbs$/, ".html"))
               .pipe(replace(/\uFEFF/ig, "")) //cut out zero width nbsp characters the compiler adds in
               .pipe(gulp.dest(config.templateOutputPath));
});

gulp.task('compileHtml:watch', function () {
    return gulp.watch(config.templates, ['compileHtml']);
});

I follow a naming convention of .page.hbs for each page to compile. Note the replacement that needs to be done on the zero width character, this caught me out the first time I did this.

There is a second task defined in there to set up a watch over the files. Running this task means whenever a template is saved, the templates are recompiled, very handy. Here's the config file:

gulp.config.js //JavaScript
module.exports = function () {
    var root = './';
    var assetRoot = root + 'wwwroot/';
    var handlebarsRoot = assetRoot + 'templates/';

    var config = {
        templatePath: handlebarsRoot,
        templatePartialPath: handlebarsRoot + 'partials',
        templateOutputPath: handlebarsRoot + 'dist',
        templates: [
            handlebarsRoot + '**/*.hbs'
        ]
    };

    return config;
};

To run the tasks, right click the gulpfile.js and click Task Runner Explorer. Now just run the compileHtml:watch task, and make some changes to your .hbs files. You will see a task kick off and your changes will be updated in the dist folder.

Conclusion

This is a neat way to separate your front end developers from your core project whilst keeping the CSS in sync. The only thing you do need to do manually is keep the HTML in sync with your Views. You can take this a step further by adding a SASS or LESS precompiler into your gulp tasks to allow use of these more advanced tools, which I'd definitely recommend.