Search code examples
node.jsexpresshandlebars.jshandlebarshelper

How do you send an object to a helper in Handlebars?


Learning handlebars and Express I'm trying to learn of a way to send an object without always having to build in the render. For example if I have:

const details = {
  version: process.env.npm_package_version,
  author: 'foobar'
}

I can send this to my footer.hbs in partials from:

app.get('/', (req, res) => {
  res.render('index', {
    details
  })
})

but looking for a way to send it to a template file and not always in a render I've read in the documentation about block helpers and tried:

// Define paths for Express config
const publicDir = path.join(__dirname, '../public')
const viewsPath = path.join(__dirname, '../templates/views')
const partialsPath = path.join(__dirname, '../templates/partials')

// Setup hbs engine and views location
app.set('view engine', 'hbs')
app.set('views', viewsPath)
hbs.registerPartials(partialsPath)

hbs.registerHelper('appDetails', () => {
  const details = {
    version: process.env.npm_package_version,
    author: 'foobar'
  }

  return details
})

but in my directory /partials from file footer.hbs I try to use the helper:

<footer>
    <p>Created by {{details.author}} | version: {{details.version}}</p>
</footer>

and it doesn't work. I've searched the site and I've read:

In my Node and Express app is there a way to send data to the partials file without having to always send it in render?


Solution

  • There are two ways a Handlebars Helper can add data to the rendering context of a template:

    1) By directly mutating the template's context or 2) By using private variables

    Example: https://codepen.io/weft_digital/pen/JjPZwvQ

    The following helper updates or adds the data points name and newData to the template's global context, and it also passes a private variable, using the data option.

    Handlebars.registerHelper('datainjection', function(context, options) {
         this.name = "Bob";
         this.newData = "Updated"
         return context.fn(this, { data: {private:"pirate"} });
    });
    

    Before you call the {{#datainjection}}{{/datainjection}} block in your template, {{name}} will be set to whatever value you pass to the render function, but every occurrence of {{name}} within or after the {{#datainjection}}{{/datainjection}} block will use the updated value, which is "Bob" in this case.

    The private variable we are passing can only be accessed within the {{#datainjection}}{{/datainjection}} block using the "@" decorator.

    For example:

    {{#datainjection}}{{@private}}{{/datainjection}}
    

    will render the string "pirate"