Search code examples
handlebars.js

Handlebars: inherited/implicit parameters


Is there an easy way how to pass a "configuration" parameter to all embedded partials in handlebars? In my case, I would like to set language by the page layout partial and would like all the embedded partials to have access to it, something like this:

@layout-en.hbs:

SOMEHOW SETTING LANG PARAMETER TO "EN"
<!DOCTYPE html>
<html lang="en">
<head>  
  <title>{{title}}</title>
</head>
<body>
 <p>Top of all English pages</p>
 {{{content}}}
 <p>Footer of all English pages</p>
</body>
</html>

@layout-fr.hbs - analogous

some-page-en.hbs:

{{#> @layout_en title="Title in English"}}

some content in English

{{#> demo}}

some more content in English

{{/@layout_en}}

some-page-fr.hbs:

{{#> @layout_fr title="Title in French"}}

some content in French

{{#> demo}}

some more content in French

{{/@layout_fr}}

Is it possible for demo to return a button with a different text when included on a page using layout_en and different when on a page using layout_fr?

Or should this be done completely differently?


Solution

  • It's possible to set a variable that is accessible to all views and partials that are rendered in the response. I am doubtful that such a variable could be set from a partial and, even if it could, I would recommend against it because I think it would make the application more difficult to troubleshoot if global variables were being set in views.

    Instead, I think a better approach would be to set such a variable in your controller logic.

    You have not mentioned what Node web server framework you are using, but I am going to assume you are using express with the express-handlebars package.

    The express API provides a locals object on the response (res), to which variables can be attached and made available to all rendered templates.

    A simple example of setting a global lang variable within our express handlers looks like:

    app.get("/en", (req, res) => {
      res.locals.lang = "en";
      res.render("some-page-en");
    });
    
    app.get("/fr", (req, res) => {
      res.locals.lang = "fr";
      res.render("some-page-fr");
    });
    

    This will allow us to use {{lang}} from within any rendered view/layout/partial and the corresponding value will be rendered.

    The problem with this approach is that it does allow for simple conditions within our template of the sort lang === "fr" ? "French content" : "English content". This is because Handlebars does not ship with a way to perform such a conditional check. A custom helper could help, however, if your application will only support the two languages - English and French - then we could replace our lang string variable with a boolean one - ex., isFrench:

    app.get("/en", (req, res) => {
      res.locals.isFrench = false;
      res.render("some-page-en");
    });
    
    app.get("/fr", (req, res) => {
      res.locals.isFrench = true;
      res.render("some-page-fr");
    });
    

    As a boolean, this variable can be used with a Handlebars #if helper. The demo partial could then look something like:

    <button type="button">
      {{#if isFrench}}
        Cliquez ici
      {{else}}
        Click here
      {{/if}}
    </button>
    

    Additionally, I would recommend using a similar pattern within a single layout file instead of potentially having an English and a French layout with mostly duplicated HTML.


    Additional note:

    {{#> demo}} on its own is not valid Handlebars syntax. The #> is for rendering Partial Blocks and those must have an accompanying closing tag: {{/demo}}. A regular (non-Block) partial would be rendered with {{> demo}}.