22

Is there a way to get positional index during iteration in ember.js?

{{#each itemsArray}}
     {{name}}
{{/each}}

I'm looking for a way to have something like:

{{#each itemsArray}}
    {{name}} - {{index}}th place.
{{/each}}

Update:

As per the comment by @ebryn the below code works without using a nested view for each item:

<script type="text/x-handlebars">    
    {{#collection contentBinding="App.peopleController"}}
        Index {{contentIndex}}: {{content.name}} <br />
    {{/collection}}
</script>​

http://jsfiddle.net/WSwna/14/

Although to have something like adjustedIndex, a nested view would be required.

Gubbi
  • 756
  • 1
  • 7
  • 18

8 Answers8

22

It's old question but still gets a lot of hits. In the current version of Ember.JS one can use _view.contentIndex to display current index inside the loop.

{{#each}}
    Index: {{_view.contentIndex}}
{{/each}}

If you need an adjusted index (for instance starting from 1) then there is a possibility of creating reusable helper increment

Ember.Handlebars.registerBoundHelper('increment', function(integer) {
    return integer + 1;
});

then you would use it in the following way

{{#each}}
    Index: {{increment _view.contentIndex}}
{{/each}}

Update

Starting with ember 1.11 you can do as well

{{#each people as |person index|}}
   Index: {{increment index}}
{{/each}}
Mateusz Nowak
  • 4,021
  • 2
  • 25
  • 37
9

In RC6 CollectionView provides contentIndex propery for each rendered view of collection. Each helper uses CollectionView in its implementation so uou can access index in this way:

{{#each itemsArray}}
    {{_view.contentIndex}}
{{/each}}
chrmod
  • 1,415
  • 12
  • 19
  • WHen i use this in each controller i get weird numbers – Tom Smykowski Aug 02 '13 at 08:31
  • this works for me - i have an array in my controller and i just do something like this: {{#each prospects}} Index {{_view.contentIndex}}: {{name}}
    {{/each}}
    – David Aug 05 '13 at 11:06
  • Without specifying the itemViewClass, we can use _view.contentIndex. But that does not work inside a #if helper within the #each. To do that we need to use: {{#each model itemViewClass="Ember.View"}} {{#if someCondition}} {{view.contentIndex}} {{/if}} {{/each}} "Specify an itemViewClass" – Champ Jun 02 '14 at 06:16
9

Actually yes you can get the position of the current index using the {{each}} helper. You have to create a view for every item in a list and then use {{_parentView.contentIndex}}.

<script type="text/x-handlebars">
{{#each App.peopleController}}
  {{#view App.PersonView contentBinding="this"}}
    Index {{_parentView.contentIndex}}: {{content.name}} {{adjustedIndex}} <br />
  {{/view}}
{{/each}}
</script>

App = Ember.Application.create();

App.peopleController = Ember.ArrayController.create({
  content: [ { name: 'Roy' }, { name: 'Mike' }, { name: 'Lucy' } ]
});

App.PersonView = Ember.View.extend(Ember.Metamorph, {
  content: null,
  // Just to show you can get the current index here too...
  adjustedIndex: function() {
    return this.getPath('_parentView.contentIndex') + 1;
  }.property()
});

See this jsFiddle for a working example.

Community
  • 1
  • 1
Roy Daniels
  • 6,309
  • 2
  • 27
  • 29
  • Could you please let me know why you need to pass in the Ember.Metamorph behaviour when extending the view? – dagda1 Mar 20 '12 at 10:12
  • 2
    You don't "need" to, but adding the `Ember.Metamorph` mixin makes the entire setup perform more like a normal `each` helper (talking speed and memory resources here) than the bloated `collection` helper. If you take a look at the source for the two and compare you'll see what I mean. For large amounts of data it makes a difference. – Roy Daniels Mar 20 '12 at 10:38
  • it appears that your code does not work with 0.9.8.1 with VIEW_PRESERVES_CONTEXT set to true – Qrilka May 28 '12 at 12:58
  • This code and fiddle are for Ember v0.9.3 as it was made 4 months ago. If you need some help with something currently, just ask. :) – Roy Daniels May 28 '12 at 14:43
  • If you were looking for 9.8 or pre-1.0 I put an [example](http://stackoverflow.com/questions/8870785/positional-index-in-ember-js-collections-iteration/12487312#12487312) at the bottom. But it really is only to be used if you REALLY need it until `@index` is available. – SciSpear Sep 21 '12 at 00:26
  • 1
    I get: Expected hash or Mixin instance, got [object Undefined] – Tom Smykowski Aug 02 '13 at 08:36
  • Methods prefixed with underscores are private and should be avoided. – Peter Wagenet Feb 19 '14 at 17:30
  • 1
    Would be excellent/really helpful of you could update this answer, should people stumble on it trying to find a working answer! :) – Andy Hayden Feb 26 '14 at 05:51
  • If you or anyone edits it I'll be more than happy to accept it. I just don't have time right now to edit answers over 2 years old! – Roy Daniels Feb 27 '14 at 22:25
6

As of Ember 9.8 and pre-1.0 you can wrap the "contentIndex" with a view in order to get at the virtual parent (the {{#each}}). If you don't add the view, your context ends up being either the main template, an item in your list or whatever you manually set with your {{#with}}. It is not impossible to get at the {{#each}} from the JS side but it is a lot more of a pain flipping through those child views.

{{#each App.peopleController}}
    {{#view}}
        Index {{view._parentView.contentIndex}}: {{name}} <br />
    {{/view}}
{{/each}}

...OR...

{{#each people in App.peopleController}}
    {{#view}}
        Index {{view._parentView.contentIndex}}: {{people.name}} <br />
    {{/view}}
{{/each}}

Just in-case you would like a fiddler.

DEMO

Note: You can create a view and do a this.get("_parentView.contentIndex") to get at the index if you want to modify the number at all.

SciSpear
  • 2,008
  • 18
  • 18
4

This isn't currently a feature of Handlebars or Ember.Handlebars. We have contentIndex available inside #collection/Ember.CollectionView. I think it's useful to support in #each too. Please file an issue at the GitHub repository: https://github.com/emberjs/ember.js/issues

Brian Mains
  • 50,520
  • 35
  • 148
  • 257
ebryn
  • 4,407
  • 1
  • 23
  • 21
  • 1
    Update to this: Handelbars has gained [support for `@index`](https://github.com/wycats/handlebars.js/commit/5e8be14d78f87e73878e4d29013482f7daff9d8a). It's not usable in Ember.Handlebars yet, but I imagine it will be sooner or later. – Jo Liss Sep 17 '12 at 19:17
2

As of Ember 1.11.0, index is an optional parameter in each blocks:

{{#each items as |item index|}}
  {{item.name}} is at index {{index}}
{{/each}}
stephen.hanson
  • 9,014
  • 2
  • 47
  • 53
1

I have modified a bit ud3323 solution using collection. Check here: http://jsfiddle.net/johngouf/WSwna/13/

<script type="text/x-handlebars">    
{{#collection contentBinding="App.peopleController"}}
  {{#view App.PersonView contentBinding="this"}}
    Index {{_parentView.contentIndex}}: {{content.name}} {{adjustedIndex}} <br />
  {{/view}}
{{/collection}}
</script>

App = Ember.Application.create();
App.peopleController = Ember.ArrayController.create({
 content: [ { name: 'Roy' }, { name: 'Mike' }, { name: 'Lucy' } ]
});

App.PersonView = Ember.View.extend({
 content: null,
 // Just to show you can get the current index here too...
 adjustedIndex: function() {
    return this.getPath('_parentView.contentIndex') + 1;
 }.property()
});

​​

Yiannis Gkoufas
  • 660
  • 6
  • 16
0

I think you could probably do something like this too

//add index property to all each queries
Handlebars.registerHelper('each', function(context, block) {
    var ret = "";
    for(var i=0, j=context.length; i<j; i++) {
        context[i].index = i;
        ret = ret + block(context[i]);
    }
    return ret;
});
Hayden Chambers
  • 747
  • 1
  • 8
  • 19