I am trying to build an html tree using Mustache.js, but it fails throwning an "Maximum call stack size exceeded", probably because an infinite recursion.
What's wrong?
var Mustache = require('mustache');
var root = {
title: 'Gar1',
children: [{title: 'gar2'}, {title: 'gar3', children: [{title: 'gar4'}]}]
};
var panelTemplate = '{{title}}<ul>{{#children}}<li>{{>panel}}</li>{{/children}}</ul>';
var partials = {panel: panelTemplate};
var output = Mustache.render(panelTemplate, root, partials);
console.log(output);
The problem is inherent to the implementation in Mustache.js (disclaimer: not sure if the problem is in the spec itself) as the algorithm looks for a property on its parent when it can't find it on the current context.
To explain it briefly: code runs on your template, outputs Gar1<ul>
and finds a {{#children}}
tag. Since your context has a children tag it outputs the <li>
and invokes the partial which will now run on the inner context {title: 'gar2'}
. When Mustache reaches your {{#children}}
tag again, it now finds out that the current context has no children
property, thus it goes one level up where it actually finds your children property and starts recursing (is this a real word?) again on itself like mad.
Two possible solutions:
1 - modify your data so that all entries have a children
property, and when a node should have no children set it to false
or null
(not an empty array) like this:
var root = {
title: 'Gar1',
children: [{title: 'gar2', children: false}, {title: 'gar3', children: [{title: 'gar4', children: false}]}]
};
2 - use Handlebars instead of Mustache, and wrap the <ul>
in an {{#if children}}
tag.
Hope this helps, I know the answer is a bit late since this has been asked.
Here is one object that needs recursion.
var obj = [
{
obj: true,
key: 'a',
value: [
{
obj: false,
key: 'a',
value: 1
},
{
obj: false,
key: 'b',
value: 2
},
{
obj: true,
key: 'c',
value: [
{
obj: false,
key: 'a',
value: 3
}
]
}
]
},
{
obj: false,
key: 'b',
value: 4
}
];
<!-- root -->
<ul>
{{#value}}
<li>
<!-- object -->
{{#obj}}
<span><b>{{key}}</b></span>
{{>object}}
{{/obj}}
<!-- value -->
{{^obj}}
<span><b>{{key}}</b> <span>{{value}}</span></span>
{{/obj}}
</li>
{{/value}}
</ul>
The first object you pass in is the root it has no key
only a value
. If the value
has a property obj
set to true
then it's an object, print out its key and call the template again recursively for its value.
If not an object then no need for recursion, simply print out.
// html is recursion.html contents
var template = Hogan.compile(html),
content = template.render({value: obj}, {object: html});
// show the rendered template
$('body').empty().append(content);
res.render('recursion', {
value: obj,
partials: {
object: 'recursion'
}
});
Btw I'm using Hogan.js for rendering the template. I don't know whether Mustache.js render supports recursion or not.