14

This is what I'd like to do in Mustache.js but not seeing how with the documentation.

var view = {items:['Mercury','Venus','Earth','Mars']};
var template = "<ul> {{#items}}<li>{{i}} - {{.}}</li>{{/items}} </ul>";
var html = Mustache.to_html(template,view);

Desired output:

<ul>
  <li>0 - Mercury</li>
  <li>1 - Venus</li>
  <li>2 - Earth</li>
  <li>3 - Mars</li>
</ul>
Dane
  • 268
  • 1
  • 2
  • 6
  • 1
    You can't. You either have to pass an object containing the id and value, or inject the value post-processing. – Gazler Dec 19 '11 at 20:55

6 Answers6

20

Looking for a fast clean solution?

Simply add an index function

var data = {
    items: [{
            name: Aliasghar
            , grade: 19
        }, {
            name: Afagh
            , grade: 20
    }]
    , index: function() {
        return ++window['INDEX']||(window['INDEX']=0);
    }
}

and your template could be like this:

{{#items}}
    {{index}} -- {{name}} is {{grade}}
{{/items}}

How it works

We add a index: function(){} to data and we use it as a normal function in template. This function adds a key to the window object which is available globally; then increases it one by one.

To use with multiple lists

Please note that if you are using multiple templates one after another you need to either reset window['INDEX'] or change it's key to something else like window['YEKI_DIGE']. Another way of doing this is by adding a resetIndex function. Here is the way:

var data = {
    items: [{
            name: Aliasghar
            , grade: 19
        }, {
            name: Afagh
            , grade: 20
    }]
    , index: function() {
        return ++window['INDEX']||(window['INDEX']=0);
    }
    , resetIndex: function() {
        window['INDEX']=null;
        return;
    }
}

and your template could be like this:

{{#items}}
    {{index}} -- {{name}} is {{grade}}
{{/items}}
{{resetIndex}}

Inspired by this answer: https://stackoverflow.com/a/10208239/257479 from dave on In Mustache, How to get the index of the current Section

Community
  • 1
  • 1
Alexar
  • 1,715
  • 4
  • 22
  • 32
18

An alternative solution, without fooling around with Mustache.js

Instead of fooling around with mustache you might as well use a <ol></ol> instead of <ul></ul>, that will prefix each item with index+1.

If you'd like you can use css to change the starting number to 0, and it will render exactly as you want. You can even change the dot after the number, to something such as " - ", and problem is solved.

<ol>
  <li>Mercury</li>
  <li>Venus</li>
  <li>Earth</li>
  <li>Mars</li>
</ol>

the above will render as:

1. Mercury
2. Venus
3. Earth
4. Mars

the Mustache.js way to do it

The proper method if you'd like to do it in mustache is to convert your array of strings to an array of objects, where index and string value is present.

// I wrote this from the back of my head, it's untested and not guaranteed
// to work without modifications, though the theory behind it is valid.


var array     = ["123","stackoverflow","abc"];
var obj_array = [];

for (idx in array)
   obj_array.push ({'index': idx, 'str': array[idx]});

var view     = {items: obj_array};
var template = "<ul>{{#items}}<li>{{index}} - {{str}}</li>{{/items}}</ul>";
var html     = Mustache.to_html(template,view);
Community
  • 1
  • 1
Filip Roséen - refp
  • 58,021
  • 18
  • 139
  • 186
  • 1
    Yeah, I think the
      method would be good in many cases but my specific need is a little more complex.
    – Dane Dec 19 '11 at 21:09
  • 3
    I was afraid explicitly adding an index to the view object might be the answer...too bad. Thanks for the answer! – Dane Dec 19 '11 at 21:10
  • @Dane I edited my post and wrote a sample implementation, if you try it out please get back to me so I can alter it if it doesn't work, or remove the comment saying that it maybe won't work, if it works. – Filip Roséen - refp Dec 19 '11 at 21:12
  • I'm sure your answer works. I was just hoping there was a way to get the idx without explicitly adding it to the view object. Maybe this will be part of Mustache 2. Thanks again for taking the time to answer my question. – Dane Dec 19 '11 at 21:17
4

If you can use handlebars.js, then you can use the partial mentioned in this gist: https://gist.github.com/1048968

Ref: In Mustache, How to get the index of the current Section

Community
  • 1
  • 1
rnella01
  • 151
  • 1
  • 1
  • 6
3

If you want to access the index multiple times per item, I would suggest something like the following.

var data = {
    items: [{
        name: Aliasghar
        , grade: 19
    }, {
        name: Afagh
        , grade: 20
    }]
    , setUpIndex: function() {
        ++window['INDEX']||(window['INDEX']=0);
        return;
    }
    , getIndex: function() {
        return window['INDEX'];
    }
    , resetIndex: function() {
        window['INDEX']=null;
        return;
    }
}
  • Use the restIndex function before iterating each list in Mustache.
  • Use the setUpIndex function at the start of each list item to set up the index for that item.
  • Use the getIndex item to access the index.

As an example, consider this.

{{resetIndex}}
{{#items}}
    {{setUpIndex}}
    {{getIndex}}. {{name}} is at index {{getIndex}}
{{/items}}

Notice how you can access index more than once per item without getting the wrong value.

teon
  • 8,321
  • 10
  • 46
  • 83
Tayyab
  • 397
  • 3
  • 15
  • in setUpIndex:: , should the return statement in the next line ? else the index values will get printed while rendering the mustache template. – teon Jul 12 '15 at 22:59
1

You can use Handlerbars.js, then

Handlebars.registerHelper("inc", function (value, options) {
    return parseInt(value) + 1;
});

To use it in HTML

{{inc @@index}}
Elaine
  • 1,210
  • 5
  • 17
  • 33
1

(Tested in node 4.4.7, moustache 2.2.1.)

If you want a nice clean functional way to do it, that doesn't involve global variables or mutating the objects themselves, use this function;

var withIds = function(list, propertyName, firstIndex) {
    firstIndex |= 0;
    return list.map( (item, idx) => {
        var augmented = Object.create(item);
        augmented[propertyName] = idx + firstIndex;
        return augmented;
    })
};

Use it when you're assembling your view;

var view = {
    peopleWithIds: withIds(people, 'id', 1) // add 'id' property to all people, starting at index 1
};

The neat thing about this approach is that it creates a new set of 'viewmodel' objects, using the old set as prototypes. You can read the person.id just as you would read person.firstName. However, this function doesn't change your people objects at all, so other code (which might have relied on the ID property not being there) will not be affected.

Steve Cooper
  • 17,836
  • 14
  • 66
  • 81