How do I use nested iterators with Mustache.js or Handlebars.js?

37,460

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}}
Share:
37,460
Eric the Red
Author by

Eric the Red

Updated on November 22, 2020

Comments

  • Eric the Red
    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
    KyleMit over 10 years
    Fiddles for Handlebars and Moustache
  • Pere
    Pere almost 10 years
    Note 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
    Samo almost 10 years
    Thanks for the clarification. In fact I did not know that.
  • Dominic
    Dominic over 9 years
    What happens if the array above also has a property called "given" which you want to reference?
  • maxbeatty
    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)