2

I'm trying to write a custom iterator in spacebars (I'm using meteor 1.1.3). The iterator is to be a sequential for loop (basically to replace my usage of #each when needed since I believe #each is not guaranteed to be sequential in its iteration).

I have tried the following:

In lib -

UI.registerHelper 'sequentialFor', () ->
  ret = ""
  for i in [0...@.length]
    id = @[i]
    ret = ret + Template.noop
  ret

noop.html -

<template name="noop">
  {{> UI.contentBlock this}}
<template>

main.html -

{{#sequentialFor ids}}
<div id="wow-{{this}}">stuff</div>
{{/sequentialFor}}

ids in the above is an array of strings passed from one of main's template helpers.

Right now it complains the the return from my UI helper is [object Object] [object Object]. For sanity's sake I know that if I replace my UI helper with:

UI.registerHelper 'sequentialFor', () ->
  //ret = ""
  //for i in [0...@.length]
    //  id = @[i]
    //  ret = ret + template
  id = @[0]
  Template.noop

I get that the div in my main.html shows up with the appropriate id as a part of its id attribute as desired. However, I can't seem to make the for loop work.

I can't simply return the div in main.html directly from the helper because I have a lot of divs that I need to wrap with my new iterator, each of which has very different attributes.

I guess the simple question is, how do I define my own block iterator (akin to #each) in spacebars?

The more difficult question may be, what is wrong with my approach above?

I have considered a wide array of resources but have only the found the following to be very helpful: How to pass an object from to a block helper back to the block in meteor blaze? https://github.com/meteor/meteor/wiki/Using-Blaze https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md Iterating over basic “for” loop using Handlebars.js

NOTE I'm using coffeescript

Community
  • 1
  • 1
John Cast
  • 1,445
  • 2
  • 14
  • 34

2 Answers2

1

I managed to get a custom iterator using a recursive technique similar to what you might use in Haskell or Lisp:

<body>
  {{#countdown n=5}}
    <p>item {{this}}</p>
  {{/countdown}}
</body>

<template name="countdown">
  {{#if positive}}
    {{> Template.contentBlock n}}
    {{#countdown n=nMinusOne}}
      {{> Template.contentBlock this}}
    {{/countdown}}
  {{/if}}
</template>
Template.countdown.helpers({
  positive: function () {return this.n > 0;},
  nMinusOne: function () {return this.n - 1;}
});

See meteorpad.

The performance is probably far worse than the usual {{#each}}.

user3374348
  • 4,091
  • 13
  • 28
  • Thanks for the detailed answer. I think it's the best one yet. However, I'm not going to mark it as the solution until I find out for sure that there isn't a standard way to write a custom iterator (as there used to be one). Up-voting either way. – John Cast May 07 '15 at 20:48
0

It appears to me that you want to create a <div> for each of an array of IDs (correct me if I'm wrong). This is how I would go about it, no custom iterator necessary:

Template.registerHelper('ids', function(arrayWithIds) {
    if (!arrayWithIds) return [];
    // do some sorting or whatever with arrayWithIds, for example:
    var arrayOfIds = _.map(arrayWithIds, function(obj) {
        return obj._id;
    });
    return arrayOfIds;
});

Then in main.html:

{{#each ids someDataSetWithIds}}
    // `someDataSetWithIds` is the helper's parameter
    // `this` in each case is an ID
    <div id="wow-{{this}}"></div>
{{/each}}

If your helper returns an object, you would use this._id in the template, instead. Did I misunderstand what you're trying to achieve?

pfkurtz
  • 465
  • 3
  • 7
  • I appreciate the answer, however this does not address what I'm asking. I can already create these divs (one per id) as you do in your answer. The issue I'm trying to address is that (to my knowledge) spacebars doesn't guarantee that #each will always be synchronous (even though using its current implementation it is). Guaranteed sequential iteration is very important for my application and so I am trying to write a sequential iterator to take the place of the standard #each iterator found in spacebars. – John Cast Apr 22 '15 at 22:00
  • Even if my reason for creating this iterator seems paranoid, feel free to approach my question from the standpoint of how does one in general create/recreate the spacebars iterator {{#each}} – John Cast Apr 22 '15 at 22:06