How can I get the index of an array in a Meteor template each loop?
Asked Answered
S

6

30

Say I have an object, someObject:

{
  foo: "apple",
  myArray: ["abc", "def"]
}

And a template helper that looks like this (and works fine):

getArray: function(){
  var self = this;
  self.myArray = self.myArray || [];    
  return self.myArray;
}

How should I construct the html to get the array index?

I've tried:

<template name="someObject"> // takes someObject as data
  {{#each getArray}}
    <div class="item" data-value="{{WHAT GOES HERE?}}">{{this}}</div>
  {{/each}}
</template>

In which case this successfully returns "abc" and "def". Which is good. But how can I get the index of the array to put into the attribute data-value?

I've tried this.index directly but it's undefined. I also tried using a helper:

<template name="someObject"> // takes someObject as data
  {{#each getArray}}
    <div class="item" data-value="{{getindex}}">{{this}}</div>
  {{/each}}
</template>

but in this helper getIndex when I console.log out this I see:

String {0: "a", 1: "b", 2: "c", length: 3}
String {0: "d", 1: "e", 2: "f", length: 3}

Is it possible to get the index?

Soekarno answered 14/6, 2014 at 23:6 Comment(0)
D
59

meteor >= 1.2

Spacebars gained a lot of functionality in 1.2, including a native @index. Helpers are no longer needed to solve this problem - you can simply do this:

{{#each getArray}}
  <div class="item" data-value="{{@index}}">{{this}}</div>
{{/each}}

or, if you want to use the index inside a helper:

{{#each getArray}}
  <div class="item" data-value="{{someHelper @index}}">{{this}}</div>
{{/each}}

meteor < 1.2

Sometime in the future, spacebars may offer the ability to determine the index directly in the template. However, as of this writing, the only way to get the index is to modify the result returned by the helper. For example you could have getArray return an array of objects which contain a value and an index, like this:

getArray: function() {
  var self = this;
  self.myArray = self.myArray || [];
  return _.map(self.myArray, function(value, index){
    return {value: value, index: index};
  });
}

And the template could use the index like this:

<template name="someObject">
  {{#each getArray}}
    <div class="item" data-value="{{index}}">{{value}}</div>
  {{/each}}
</template>

Also see this answer for a similar example with cursors.

It's worth mentioning that you probably don't need to store the index in the DOM itself via data-value, unless it's needed by an external plugin. As you can see in the example below, each item has a context with an index value. For more information, see this blog post.

Template.someObject.events({
  'click .item': function() {
    console.log(this.index);
  }
});
Dialectics answered 15/6, 2014 at 5:13 Comment(4)
Thanks for clearing that up. Note. the data attribute is required for something out of my ability (or desire) to edit so I have no choice.Soekarno
Tried this on Meteor 1.2.0.2 and @index still returns undefined.Sac
How would you display @index + 1? Eg. start from 1 rather than zero?Ticket
You'll need to use a helper. In the example above, someHelper could be implemented to return the value of its argument + 1.Dialectics
T
20

You can make this a reusable helper, too. It's handy to have:

JS:

UI.registerHelper('addIndex', function (all) {
    return _.map(all, function(val, index) {
        return {index: index, value: val};
    });
});

HTML:

{{#each addIndex somearray}}
<div>
   {{index}}: {{value}}
</div>
{{/each}}
Thalassa answered 15/6, 2014 at 5:24 Comment(0)
S
9

This change is coming and you will be able to do {{@index}}. When meteor supports the most recent version of handlebars.

Singlephase answered 4/5, 2015 at 19:39 Comment(2)
At the time I wrote this it was not released yet. Probably that is still the case :/Singlephase
This was release as part of Meteor 1.2Bulgaria
J
1

You can change getArray to return an array of tupples and store the index there.

Janenejanenna answered 14/6, 2014 at 23:59 Comment(0)
K
0

Here is an example of how you can just add index to the object and as long as you didnt have a key named index before it shouldnt obstruct anything this way works only with arrays of objects. Now if you have a array of values you should use Christan Fritz answer

UI.registerHelper("withIndex", function(obj) {
  obj = obj || [];
  _.each(obj, function (object, index) {
    obj[index].index = index;
  });
  return obj;
});

{#each withIndex fields}}
   <div class="form-group field" data-index="{{index}}">
      <label for="{{name}}">{{title}}</label> 
    </div>
{{/each}}
Katelynnkaterina answered 28/7, 2015 at 22:9 Comment(0)
B
0

You can do it with underscore too, assuming that you have subscribed your template to array of objects

Template.yourTemplate.helpers({
  objectsWithIndex: function() {
    _.map(this.objects, function(value, key) {
      return _.extend(value, {
        index: key
      });
    });
    return this.objects;
  }
});

and in your html...

<template name="someObject">
  {{#each objectsWithIndex}}
    <div class="item" data-value="{{index}}">{{value}}</div>
  {{/each}}
</template>
Biyearly answered 24/8, 2015 at 17:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.