How to combine JSON and Handlebars from partials into HTML with Gulp?

3.3k views Asked by At

I'm building a static site using Handlebars and Gulp. Here's my folder structure:

app/
    content/
        intro.json
        header.json
        faq.json
        features.json
        footer.json
    templates/
        home.hbs
        partials/
            home-section.hbs
            header.hbs
            footer.hbs
    index.html
Gulpfile.js

The content of home.hbs is this:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test</title>
</head>
<body>
    {{> header}}
    {{> home-section}}
    {{> home-section}}
    {{> home-section}}
    {{> footer}}
</body>
</html>

I want to pass in intro.json, faq.json, and features.json to each of the home-section partials, and header.json to header and footer.json to footer.

This is what I have in my Gulpfile so far:

var gulp = require('gulp');
var handlebars = require('gulp-compile-handlebars');
var rename = require('gulp-rename');

gulp.task('html', function() {
  return gulp.src('./app/templates/*.hbs')
    .pipe(handlebars({}, {
            ignorePartials: true,
            batch: ['./app/templates/partials']
          }))
    .pipe(rename({
            extname: '.html'
          }))
    .pipe(gulp.dest('build'));
});

I haven't been able to figure out how to pass more than one JSON file at a time, especially to the home-sections. Thanks in advance!

2

There are 2 answers

2
dstaley On BEST ANSWER

The first parameter to handlebars is your global context, available to all your templates. You can load your individual JSON files into a context object, and use that as the first parameter.

(There's definitely better ways to do this, but hey, it's quick and easy!)

var infoData = require('./app/content/info.json');
var faqData = require('./app/content/faq.json');
var featuresData = require('./app/content/features.json');

You can then pass these objects through the global context to your handlebars function

.pipe(handlebars({ info: infoData, faq: faqData, features: featuresData }))

Once the data is inside your context, you can access it like this:

{{> home-section content=info }}
{{> home-section content=faq }}
{{> home-section content=features }}

Inside your home-section partial, you'll have a content object that will contain the data of the file you passed into it. So, if your info.json file looked like this:

{ "header": "Info", "details": "This is some information" }

Your home-content.hbs partial could then access the data like this:

<h2>{{ content.header }}</h2>
<p>{{ content.details }}</p>
0
nem035 On

Unfortunately, gulp-compile-handlerbars function only takes two arguments, the first one being all the data passed into your templates. This means you have to load up all your json files together and pass them as a single object.

You can do this with a small helper like:

function requireJsons(fileNames) {
  return fileNames.reduce(function(jsons, fileName) {
    jsons[fileName] = require('app/content/' + fileNames[i] + '.json');
    return jsons;
  }, {});
}

Which you can use to build the data object for all your templates:

var data = requireJsons(['intro', 'header', 'faq', 'features', 'footer']);

gulp.task('html', function() {
  return gulp.src('./app/templates/*.hbs')
    .pipe(handlebars(data, {
      // ...

If you always need to load all the json files from app/content directory, you can use readdirSync to obtain all .json file names and then pass them to requireJsons:

var path = require('path');
var fileNames = fs.readdirSync('app/content')
  .filter(function(fileName) {
    return path.extname(fileName) === '.json';
  });
var data = requireJsons(fileNames);

Of course, if speed is important, you could combine the two into one method that loads the jsons and builds the data object in a single pass.


Another option is to possibly compile each template individually and pass appropriate data into each compilation. A tool like gulp-foreach would be helpful.