limit results of each in handlebars.js

"each" is no longer very simple: https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/base.js#L99

That's because each now supports a whole host of loop information you probably want to still have access to.

So limiting the data early on is probably preferable if you don't want to reimplement the much more complicated each. You could also try using a subexpression within each (i.e. a {{#each (limit data 6)}} if you're using the latest version of handlebars.


I think you have two options:

  1. Limit the size of your collection before handing it to Handlebars.
  2. Write your own block helper that lets you specify a limit.

The actual each implementation is pretty simple so adapting it to include an upper limit is fairly straight forward:

// Warning: untested code
Handlebars.registerHelper('each_upto', function(ary, max, options) {
    if(!ary || ary.length == 0)
        return options.inverse(this);

    var result = [ ];
    for(var i = 0; i < max && i < ary.length; ++i)
        result.push(options.fn(ary[i]));
    return result.join('');
});

Then in your template:

<script id="tweets-template" type="text/x-handlebars-template" >
    {{#each_upto this 5}}
        <li>
            <p>{{tweet}}</p>
            <span id="author">{{author}}<span/>
        </li>
    {{/each_upto}}
</script>

I agree that duplicating each is currently not a good idea, as Dtipson says.

His proposed approach with the limit helper is indeed the best way IMO, here is the code needed to implement it:

// limit an array to a maximum of elements (from the start)
Handlebars.registerHelper('limit', function (arr, limit) {
  if (!Array.isArray(arr)) { return []; }
  return arr.slice(0, limit);
});

And then in your template (assuming your array is cart.products):

{{#each (limit cart.products 5)}}
  <li>Index is {{@index}} - element is {{this}}</li>
{{/each}}

You need a recent handlebars version that supports sub-expressions for this to work of course.