How do I use nested iterators with Mustache.js or Handlebars.js?
Solution 1
You can nest sections easily with lists of objects. Use a data structure where families
is a list that has an object members
that has a list of any objects (or even more lists)like:
{
"families" : [
{
"surname": "Jones",
"members": [
{"given": "Jim"},
{"given": "John"},
{"given": "Jill"}
]
},
{
"surname": "Smith",
"members": [
{"given": "Steve"},
{"given": "Sally"}
]
}
]
}
You would be able to populate a template like:
<ul>
{{#families}}
<li>{{surname}}
<ul>
{{#members}}
<li>{{given}}</li>
{{/members}}
</ul>
</li>
{{/families}}
</ul>
jsFiddle is currently down so here's the full working HTML with JS:
<!DOCTYPE html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.3.0/mustache.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
$(function() {
var tpl = $('#fam').html(),
data = {
"families" : [
{
"surname": "Jones",
"members": [
{"given": "Jim"},
{"given": "John"},
{"given": "Jill"}
]
},
{
"surname": "Smith",
"members": [
{"given": "Steve"},
{"given": "Sally"}
]
}
]
},
html = Mustache.to_html(tpl, data);
$("#main").append(html);
});
</script>
</head>
<div id="main"></div>
<script type="template/text" id="fam">
<ul>
{{#families}}
<li>{{surname}}
<ul>
{{#members}}
<li>{{given}}</li>
{{/members}}
</ul>
</li>
{{/families}}
</ul>
</script>
Solution 2
Sorry I'm a little late in the game here. The accepted answer is great but I wanted to add an answer that I think is also useful, especially if you are iterating over simple row/column arrays.
When you're working with nested handlebar paths, you can use ../
to refer to the parent template context (see here for more information).
So for your example, you could do:
{{#each families}}
{{#each members}}
<p>{{../surname}}</p>
<p>{{given}}</p>
{{/each}}
{{/each}}
This was especially useful for me because I was making a grid and I wanted to give each square a class name corresponding to its row and column position. So if rows
and columns
, simply return arrays, I can do this:
<tbody>
{{#each rows}}
<tr>
{{#each columns}}
<td class="{{this}}{{../this}}"></td>
{{/each}}
</tr>
{{/each}}
</tbody>
Update
This solution is for Handlebars. A comment below explains why it will not work in Mustache.
Solution 3
Great answer @maxbeatty.
I just wanted to add another example if anyone have the same problem and can't understand the above solution.
First I have one dimensional array which I wanted to split on every 4 elements:
// this is the one dimensional data we have from let's say a mysql query
var array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', ...];
// think of it as [[], [], [], [], [], ...]
// but instead we'll be adding a dummy object with a dummyKey
// since we need a key to iterate on
var jagged = [];
var size = 4, // this is the size of each block
total = array.length / block; // total count of all blocks
// slice the initial one dimensional array into blocks of 4 elements each
for (var i=0; i < total; i++) {
jagged.push({dummyKey: array.slice(i*size, (i+1)*size)});
}
Now if we pass jagged
into our view we can iterate it like that:
<ul>
{{#jagged}}
<li>
<ul>
{{#dummyKey}}
<li>{{.}}</li>
{{/dummyKey}}
</ul>
</li>
{{/jagged}}
</ul>
If we have our initial array filled with objects:
var array = [{key1: 'a',
key2: 'b'},
{key1: 'c',
key2: 'd'},
{key1: 'e',
key2: 'f'},
...
];
Then in our template we'll have:
<ul>
{{#jagged}}
<li>
<ul>
{{#dummyKey}}
<li>{{key1}} - {{key2}}</li>
{{/dummyKey}}
</ul>
</li>
{{/jagged}}
</ul>
Solution 4
For a dataset like below:
{
rows:
["1A", "1B"],
["2A", "2B"],
["3A", "3B"]
}
following will work in mustachejs:
<tbody>
{{#rows}}
<tr>
{{#.}}
<td>{{.}}</td>
{{/.}}
</tr>
{{/rows}}
</tbody>
Solution 5
You can reference the parent loop variable and index with the ../
operator:
{{#each families}}
{{#each this.members}}
<p>Current member: {{ this.name }} (this = inner loop itterator)</p>
<p>Current family: {{ ../this.title }} (../this = parent loop itterator)</p>
<p>Current member index: {{ @index }}</p>
<p>Current family index: {{ @../index }}</p>
{{/each}}
{{/each}}
Eric the Red
Updated on November 22, 2020Comments
-
Eric the Red over 3 years
I would like to use handlebars.js or mustache.js to iterate over a list of families, and then iterate over that family's members. Inside of both loops, I want to display properties of both. However, once I get into the second iteration, none of the family variables are visible.
{{#each families}} {{#each members}} <p>{{ ( here I want a family name property ) }}</p> <p>{{ ( here I want a member name property ) }}</p> {{/each}} {{/each}}
Is this possible? I'd greatly appreciate any help!
-
KyleMit over 10 yearsFiddles for Handlebars and Moustache
-
Pere almost 10 yearsNote that this only works on Handlebars, not on Mustache. Mustache will pick up only the "closest" variable with the same name in nested paths, and to my knowledge there's no way to access a variable with the same name declared in an outer level. Using the
../
syntax in a Mustache template will not throw an error, but won't pick up any value. -
Samo almost 10 yearsThanks for the clarification. In fact I did not know that.
-
Dominic over 9 yearsWhat happens if the array above also has a property called "given" which you want to reference?
-
maxbeatty over 9 years@DominicTobias Mustache only allows you to reference variables from the current scope. You can have a
given
variable at each level but cannot access parent levels' variables. jsfiddle.net/9jpnpw7a/1 (Handlebars would probably let you do this)