This weekend I learned some unfamiliar behaviors with the way Handlebars handles nested variable scopes. I typically use Handlebars via the handlebars-rust implementation which aims to maintain nearly one to one compatibility with the JavaScript implementation. They have block scope helpers such as #each and #with, both of which create an inner scope for variable resolution. Unfortunately, the syntax can be quite unintuitive for accessing outer scope once in those nested scopes.

Handlebars is a largely declarative templating syntax which uses curlybraces such as {{var}} for variable and helper substitution. The #each helper is important for loops, imagine the following data structure:

{
  "repos" : [
    {
      "name" : "otto"
    },
    {
      "name" : "l4bsd"
    }
  ],
  "mood" : "cool"
}

This could be rendered into a list on a page via:

<ul>
    {{#each data.repos}}
        <li>{{name}}</li>
    {{/each}}
</ul>

Inside the #each block the values of the indexed object become the scope for variable resolution, such that {{name}} actually refers to data.repos[i].name. This presents problems when the template must refer to outer scope variables, such as mood. In the Rust implementation this variable resolution can be accomplished through a path traversal style syntax such as:

<ul>
    {{#each data.repos}}
        <li>{{name}} is {{../data.mood}}</li>
    {{/each}}
</ul>

The ../data.mood is all that’s needed to refer to the variable in the outer scope of variables. Not what I expected at all, and the only reason I found it was because I found an old issue which alluded to the syntax and gave it a try.