Handlebars/Mustache - Is there a built in way to loop through the properties of an object?

134,371

Solution 1

Built-in support since Handlebars 1.0rc1

Support for this functionality has been added to Handlebars.js, so there is no more need for external helpers.

How to use it

For arrays:

{{#each myArray}}
    Index: {{@index}} Value = {{this}}
{{/each}}

For objects:

{{#each myObject}}
    Key: {{@key}} Value = {{this}}
{{/each}}

Note that only properties passing the hasOwnProperty test will be enumerated.

Solution 2

It's actually quite easy to implement as a helper:

Handlebars.registerHelper('eachProperty', function(context, options) {
    var ret = "";
    for(var prop in context)
    {
        ret = ret + options.fn({property:prop,value:context[prop]});
    }
    return ret;
});

Then using it like so:

{{#eachProperty object}}
    {{property}}: {{value}}<br/>
{{/eachProperty }}

Solution 3

EDIT: Handlebars now has a built-in way of accomplishing this; see the selected answer above. When working with plain Mustache, the below still applies.

Mustache can iterate over items in an array. So I'd suggest creating a separate data object formatted in a way Mustache can work with:

var o = {
  bob : 'For sure',
  roger: 'Unknown',
  donkey: 'What an ass'
},
mustacheFormattedData = { 'people' : [] };

for (var prop in o){
  if (o.hasOwnProperty(prop)){
    mustacheFormattedData['people'].push({
      'key' : prop,
      'value' : o[prop]
     });
  }
}

Now, your Mustache template would be something like:

{{#people}}
  {{key}} : {{value}}
{{/people}}

Check out the "Non-Empty Lists" section here: https://github.com/janl/mustache.js

Solution 4

This is @Ben's answer updated for use with Ember...note you have to use Ember.get because context is passed in as a String.

Ember.Handlebars.registerHelper('eachProperty', function(context, options) {
  var ret = "";
  var newContext = Ember.get(this, context);
  for(var prop in newContext)
  {
    if (newContext.hasOwnProperty(prop)) {
      ret = ret + options.fn({property:prop,value:newContext[prop]});
    }
  }
  return ret;
});

Template:

{{#eachProperty object}}
  {{key}}: {{value}}<br/>
{{/eachProperty }}

Solution 5

@Amit's answer is good because it will work in both Mustache and Handlebars.

As far as Handlebars-only solutions, I've seen a few and I like the each_with_key block helper at https://gist.github.com/1371586 the best.

  • It allows you to iterate over object literals without having to restructure them first, and
  • It gives you control over what you call the key variable. With many other solutions you have to be careful about using object keys named 'key', or 'property', etc.
Share:
134,371

Related videos on Youtube

Ben
Author by

Ben

Updated on July 25, 2022

Comments

  • Ben
    Ben almost 2 years

    As the title of question says, is there a mustache/handlebars way of looping through an object properties?

    So with

    var o = {
      bob : 'For sure',
      roger: 'Unknown',
      donkey: 'What an ass'
    }
    

    Can I then do something in the template engine that would be equivalent to

    for(var prop in o)
    {
        // with say, prop a variable in the template and value the property value
    }
    

    ?

  • Ben
    Ben over 12 years
    Ended up going with your suggestion as I need to pass some additional sub properties anyway. Thanks for the help!
  • sirentian
    sirentian over 11 years
    Nice find. Just a warning to other readers: the "key_value" helper in this gist has a bug in it. Read the comments for how to fix it.
  • monkeyboy
    monkeyboy over 11 years
    Looks good, do you need to add a hasOwnProperty check inside the loop so you're not iterating over prototype properties?
  • flynfish
    flynfish almost 11 years
    Great solution @Ben. In case anyone is trying to use this with Ember, see my answer below for the solution to get it working.
  • Ben
    Ben almost 11 years
    Thanks @flynfish. context is a string in Ember?? that seems.. somewhat odd.
  • flynfish
    flynfish almost 11 years
    Yea I'm not really sure since I am new to Ember and still trying to find my way around it.
  • Rafi
    Rafi almost 11 years
    This doesn't work with nested objects. I tried {{#each myObject}} <li> {{data.title}} </li> and it returns <li></li>
  • Waihon Yew
    Waihon Yew almost 11 years
    @Rafi: one cannot make much sense of that without knowing your data structure though.
  • Matt
    Matt over 10 years
    Thanks very much, your idea saved me another day of looking of alternatives. This line is the key mustacheFormattedData = { 'people' : [] };
  • sjkp
    sjkp over 10 years
    I had a list of strings i used as input for my template, it worked greate with the following template code {{#.}} <li>{{this}}</li> {{/.}}
  • mcw
    mcw over 10 years
    @Ben: can you make this the accepted answer? The older answers imply that this is not actually built-in, which was true at the time, but are now very misleading.
  • nevyn
    nevyn over 10 years
    @Rafi: don't you mean {{this.title}}?
  • qodeninja
    qodeninja over 10 years
    Ok this slightly doesnt make sense. As I understand {{#each <key>}} has to be an explicit key, where did you get myArray and myObject from? Your code would not work on a top-level object that doesnt have pre-defined keys. How do you this generically, over an object?
  • Waihon Yew
    Waihon Yew over 10 years
    @qodeninja: Simple: the same way you refer to the values in the examples above -- with {{#each this}}. Your choice of terms is also confusing (what makes one object "top level" and another not? what are "pre-defined" keys exactly? etc), so you might want to revisit these concepts.
  • qodeninja
    qodeninja over 10 years
    @Jon thanks for explaining this, I'm just getting into handlebars. Top-level as in not a subobject. data = { key1: val1, key2 : val2, sub1 : { } }, pre-defined keys, as in you have to explicitly specify the keys in the template. If you dont know what the keys are how do you do this? But you explained by using this so I'll try that! But the mystery is where you go myArray and myObject from, you should update your code to reflect an example that will work to help newbies who dont understand what you're implying
  • sergserg
    sergserg over 9 years
    Not working for me. Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed {0: Name can't be blank, 1: Zip code can't be blank, 2: Company type can't be blank, 3: Address line can't be blank, 4: Country can't be blank, 5: State can't be blank}
  • Waihon Yew
    Waihon Yew over 9 years
    @Serg: Handlebars? What version?
  • sergserg
    sergserg over 9 years
    Latest of this comment. 1.3.0.
  • red888
    red888 over 9 years
    How would you do this with an array of "o" objects?
  • Renars Sirotins
    Renars Sirotins almost 9 years
    if not mistaken then only with v1.1.0 this is available, but great answer thanks.
  • Marco Prins
    Marco Prins almost 9 years
    How do you do this for only a specific whitelist of properties?
  • Waihon Yew
    Waihon Yew almost 9 years
    @MarcoPrins: In Handlebars at least, AFAIK you write your own helper. Handlebars is notorious for choosing to provide such a minimal amount of logic built-in that you cannot even stitch a whitelist together.
  • Peter Krauss
    Peter Krauss about 7 years
    Using node and mustache --version is 2.3... ERROR: Error: Unclosed section "each paths". Where paths is the myObject name.
  • Andy
    Andy almost 5 years
    What if you need to do some manipulation or calculation to @key? For example the equivalent of @key - 1. How is that possible? In a lot of templates you need to do things such as checking the previous value within an array to avoid displaying duplicate headings, etc.