Dev

RenderHBS.js

A client-side Handlebars data rendering utility

While there’s certainly no shortage of shiny new JavaScript templating frameworks, Handlebars.js is still a dope solution for a wide assortment of projects.

Don’t be fooled by the antiquated docs site – the lib is actively maintained and leveraged across a nice collection of popular CMS/content platforms, like Ghost.

Simplicity + Flexibility

What makes Handlebars so awesome is its simplicity and flexibility. The logic-less, mustache-like templates bring much-needed sanity & structure to client-side views, while precompiled .hbs files offer a feature-rich partials system when integrated into a build tool like Gulp.

HBS Templates

While your Handlebars templates can be housed across on-page script tags, in my experience, it’s far better to organize them as separate .hbs files named after the component they represent. To assist with this approach, here’s a handy and reusable js module I cooked up.

Peweeeeeee!

RenderHBS.js

The utility:

  1. Snags a template file via the fetch api
  2. Compiles a data source via Handlbars.compile
  3. Renders the final html to a specified DOM element upon successful resolution of the promise.

/**
 * Render HBS
 * @useage RenderHBS.init(yoDATA, yoRenderEl, yoTemplate)
 */
const RenderHBS = (() => {

  return {

    /**
     * Init
     * Just an init that passes params to the render function
     * @param data {obj} - The data source
     * @param renderEl {string} - DOM Element
     * @pram template {string} - Path to the hbs file
     */
    init(data, renderEl, template){
      this.render(data, renderEl, template);
    },

    /**
     * Render HBS Template to
     * Renders our hbs template with our data
     * @param {hbsTemplate} string - path to template
     * @param {renderEl} element - element to render to
     * @param {Object} data - data object
     */
    render(data, renderEl, hbsTemplate) {
      RenderHBS.getTemplate(hbsTemplate, function(template) {
        renderEl.insertAdjacentHTML('beforeend', template(data));
      });
    },

    /**
     * Get Template
     * Get's an external HBS template via fetch and compiles
     * with our data.
     * @param {string} path - path to our template file
     * @param {function} callback - our callback function to pass the response
     */
     getTemplate(path, callback) {
       var source, template;

       fetch(path)
        .then(response => response.text())
        .then( (data) => {
          source = data;
          template = Handlebars.compile(source);
          if (callback) callback(template);
        })
        .catch((error) => {
          console.log(error);
      });
    }
  };
})();

export default RenderHBS;

Usage

Just run the module by passing 3 params to the init() method.

  1. @param data {obj} – The data source
  2. @param renderEl {string} – DOM Element
  3. @pram template {string} – Path to the .hbs template

app.js


 /**
  * Render Articles
  */
import RenderHBS from '../components/_RenderHBS.js'

const data = articles_data;
const el = document.querySelector('#js-articles');
const template ='templates/article.hbs';

RenderHBS.init(data, el, template)

With an .hbs all like

templates/article.hbs


 {{#this}}
 <article class="post">
   <a class="post__link" href="{{article_link}}">
     <figure class="post__figure">
       <img class="post__img" src="{{article_image_src}}" alt="">
     </figure>
     <header class="post__header">
       <h3 class="post__title">{{article_title}}
       <p class="post__excerpt">{{article_excerpt}}

</header> </a> <r/article> {{/this}}

With a data source looking like


  var articles_data = [
  {
    "article_title": "Some Post About Some Thing",
    "article_image_src": "http://yomom.com/images/yomom.jpg",
    "article_author": "Carlos Danger",
    "article_excerpt": "This is a article that must be told...",
    "article_url": "http://yomom.com/some-article",
  },
  ...
]

Github

You can go snag a copy of renderHBS.js on the GitHub, right hizzy.

Includes es5 and es6 versions.

Peeweeeeee!

HBS Partials and Precompiling

As mentioned, you can further enhance your project organization by leveraging Handlebars for partials that can be called directly by other templates, like so:

index.hbs


{{> app-head}}
{{> app-header}}
{{> mast}}
{{> articles}}
{{> app-footer}}

Following a project structure like



|- pages - Pages that compile to .html files in dist
  |- index.hbs
  |- about.hbs
  |- etc...
|- partials
  |- _app-head.hbs
  |- _app-header.hbs
  |- _etc...

These partials can then precompiled into .html files via gulp-compile-handlebars, like so:

gulpfile.js


/**
 * Handlebars Partials
 */
const handlebars = require('gulp-compile-handlebars');

gulp.task('build-hbs', () => {

  return gulp.src('src/pages/*.hbs')
    .pipe(handlebars({}, {
      ignorePartials: true,
      batch: ['src/partials']
    }))
    .pipe(rename({
      extname: '.html'
    }))
    .pipe(gulp.dest('dist'));
});

A nifty way to organize static projects, yeah? Plus, partials provide support for variables and conditionals and such, allowing for more dynamic content. For example, lets call a header partial and pass a modifier class to the app-header element.


// index.hbs
{{> header header_class="is-clear"}}

// partials/_header.hbs
<header class="app-header {{header_class}}"></header>

Now go ham with them .hbs partials bruv…

Read Next

ReadMore.js

Read Story