RECURSION IN HANDLEBARS WITH DEPTH LEVEL
Logic-less templates in Handlebars.js are fantastic when you are making templates for JavaScript components, or doing code generation using JavaScript. Handlebars is easy to use, and its features are necessarily limited. One feature that is really missing is recursion. Although Handlebars a helper for traversing an array, it is difficult to implement recursion with, let alone recursion with a depth level. In this guide we’ll show you how to implement recursion with a depth level in Handlebars.js.
Let’s look at the ordinary #each block helper in Handlebars.js first. If we compile the following simple template:
Then we can execute it like so:
var people = [
{ firstname: "John", lastname: "Smith" },
{ firstname: "Mary", lastname: "Robinson" }
];
Handlebars.templates.people({ people: people });
Using Handlebars Partials
What if we had the following model:
var people = [
{ firstname: "John", lastname: "Smith", friends: [
{ firstname: "Manuel", lastname: "Ortega", friends: [
{ firstname: "Max", lastname: "Kowalski", friends: [] },
{ firstname: "Don", lastname: "River", friends: [] }
]}
]},
{ firstname: "Mary", lastname: "Robinson", friends: [] }
];
Here, each person has an array of friends, and each friend has their own array of friends. Suppose we’d like to render these relationships as an HTML unordered list:
<ul>
<li>John Smith
<ul>
<li>Manuel Ortega
<ul>
<li>Max Kowalski</li>
<li>Don River</li>
</ul>
</li>
</ul>
<li>Mary Robinson</li>
</ul>
This will require recursion.
We can combine the #each block helper with partial templates. When we have the partial template call itself, then we have recursion. A partial template for a person looks like this:
And our main template is:
We’ll need to register our partial before we can render the main template. In JavaScript:
Handlebars.registerPartial('person', Handlebars.templates.person);
Handlebars.templates.people({ people: people });
Using a Recursion Helper
We’ll keep the same model, but we would like to render a different output this time:
+ John Smith
++ Manuel Ortega
+++ Max Kowalski
+++ Don River
+ Mary Robinson
In order to render the pluses, our Handlebars templates needs to know what the recursion depth is at any given time, and we don’t have access to this information when using the #each block helper and a partial. However, we can still solve this by creating our own custom helper:
buildPerson(person, level) {
if(!level) level = 1;
var str = "";
for (let i = 0; i < level; i++) str += "+";
str += Handlebars.templates.person({ person: person });
person.friends.forEach((o) => {
str = str + this.buildObject(o, level+1);
});
return new Handlebars.SafeString(str);
}
Handlebars.registerHelper('buildPerson', (person) => { return buildPerson(person); });
(Note that the custom helper returns a Handlebars SafeString to prevent Handlebars from HTML escaping the names of the people, should they contain escapable characters.)
This custom helper takes a person object and a recursion depth level. By default, the depth level will be 1, resulting in one plus sign. The helper calls a partial, which renders one person (ignoring their friends). It then calls itself recursively for the person’s friends, increasing the recursion depth by 1.
Our main template becomes:
And our partial becomes:
This strategy allows us to implement recursion with depth level.
3 comments
Archived from the site's previous comment system. New comments are closed.
Thank you for the help on this. Recently needed to implement handlerbars for some templating. I was originally using Razor but needed something lighter and easier to maintain. This helped me wrap my head around some of the logic I was trying to replace.
Awesome! Thanks for letting me know.
I am new to handlebar template engine using with nodejs and express.While doing each iteration in hbs template, how to loop through upto nth child in hbs template and how to register partials in nodejs. Another one how to use &&, || in if statement inside hbs template. It would be great if you provide with small sample.