John Cast
John Cast

Reputation: 1859

Write custom iterator for Spacebars

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 [[email protected]]
    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 [[email protected]]
    //  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

Upvotes: 2

Views: 218

Answers (2)

user3374348
user3374348

Reputation: 4101

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}}.

Upvotes: 1

pfkurtz
pfkurtz

Reputation: 514

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?

Upvotes: 0

Related Questions