在Mustache中,如何获取当前Section的索引

时间:2011-02-16 20:02:24

标签: templates mustache

我正在使用Mustache并使用数据

{ "names": [ {"name":"John"}, {"name":"Mary"} ] }

我的小胡子模板是:

{{#names}}
    {{name}}
{{/names}}

我希望能够获得数组中当前数字的索引。类似的东西:

{{#names}}
    {{name}} is {{index}}
{{/names}}

并将其打印出来

John is 1
Mary is 2

是否有可能通过Moustache获得此功能?或使用把手或其他扩展名?

12 个答案:

答案 0 :(得分:45)

这是我在JavaScript中的方式:

var idx = 0;

var data = { 
   "names": [ 
       {"name":"John"}, 
       {"name":"Mary"} 
    ],
    "idx": function() {
        return idx++;
    }
};

var html = Mustache.render(template, data);

您的模板:

{{#names}}
    {{name}} is {{idx}}
{{/names}}

答案 1 :(得分:31)

作为参考,此功能现在内置于Handlebars,它与Mustache兼容。

使用{{@index}}

{{#names}}
    {{name}} is {{@index}}
{{/names}}

约翰是0

玛丽是1

答案 2 :(得分:13)

在handlebars.js中,您可以使用辅助函数完成此操作。 (事实上​​,这里提到的把手http://yehudakatz.com/2010/09/09/announcing-handlebars-js/的一个优点是你可以使用助手而不必在调用模板之前重写对象。

所以,你可以这样做:

  var nameIndex = 0;
  Handlebars.registerHelper('name_with_index', function() {
    nameIndex++;
    return this.name + " is " + nameIndex;
  })

然后,您的模板可以是:

{{#names}}
<li>{{name_with_index}}</li>
{{/names}}

您的数据与以前相同,即:

{ "names": [ {"name":"John"}, {"name":"Mary"} ] };

你得到这个输出:

<li>John is 1</li>
<li>Mary is 2</li>

为了使其真正起作用,每次渲染模板时都需要重置nameIndex,所以为了做到这一点,你可以在列表的开头有一个重置助手。所以完整的代码看起来像这样:

  var data = { "names": [ {"name":"John"}, {"name":"Mary"} ] };
  var templateSource = "<ul>{{reset_index}}{{#names}}<li>{{name_with_index}}</li>{{/names}}</ul>";
  var template = Handlebars.compile(templateSource);

  var helpers = function() {
    var nameIndex = 0;
    Handlebars.registerHelper('name_with_index', function() {
      nameIndex++;
      return this.name + " is " + nameIndex;
    });
    Handlebars.registerHelper('reset_index', function() {
      nameIndex = 0;
    })
  }();

  var htmlResult= template(data);
  $('#target').html(htmlResult);

  var htmlResult2= template(data);
  $('#target2').html(htmlResult2);

(这可以正确地渲染模板两次。)

答案 3 :(得分:6)

Mustache的一个更好的方法是使用一个使用indexOf得到索引的函数:

var data = { 
   names: [ {"name":"John"}, {"name":"Mary"} ],
    index: function() {
        return data.names.indexOf(this);
    }
};

var html = Mustache.render(template, data);

在你的模板中:

{{#names}}
    {{name}} is {{index}}
{{/names}}

缺点是这个index函数具有硬编码data.names的数组,以使其更具动态性,我们可以使用其他函数:

var data = { 
   names: [ {"name":"John"}, {"name":"Mary"} ],
    index: function() {
        return function(array, render) {
            return data[array].indexOf(this);
        }
    }
};

var html = Mustache.render(template, data);

模板中的用法:

{{#names}}
    {{name}} is {{#index}}names{{/index}}
{{/names}}

另一个小缺点是你必须在这个例子names{{#index}}names{{/index}}中将数组名称传递给索引函数。

答案 4 :(得分:4)

这里的好帮手:

// {{#each_with_index records}}
//  <li class="legend_item{{index}}"><span></span>{{Name}}</li>
// {{/each_with_index}}

Handlebars.registerHelper("each_with_index", function(array, fn) {
    var buffer = "";
    for (var i = 0, j = array.length; i < j; i++) {
        var item = array[i];

        // stick an index property onto the item, starting with 1, may make configurable later
        item.index = i+1;

        // show the inside of the block
        buffer += fn(item);
    }

    // return the finished buffer
    return buffer;

});

来源:https://gist.github.com/1048968

答案 5 :(得分:1)

如果你可以控制JSON字符串的输出,那么试试这个。

{ "names": [ {"name":"John", "index":"1"}, {"name":"Mary", "index":"2"} ] }

因此,当您创建JSON字符串时,请将索引添加为每个对象的另一个属性。

答案 6 :(得分:1)

我的主要用例是能够在项目上放置“主动”课程。我把“等于”帮助器和“index_for_each”帮助器结合在一起,但它太复杂了。

相反,我想出了以下基本的“主动”助手。它非常具体,但对于任何菜单/选择列表场景来说都是如此常见的用例:

用法:

  <ul>
    {{{active 2}}}
    {{#each menuItem}}
      <li class="{{{active}}}">{{this}}</li>
    {{/each}}
  </div>

会使第3个菜单项具有“class ='active'”。

帮助程序在这里(这是CoffeeScript版本):

active = 0
cur = 0
handlebars.registerHelper 'active', (item, context) ->
  if arguments.length == 2
    active = item
    cur = 0
    ''
  else if cur++ == active
    'active'

答案 7 :(得分:1)

只要您没有进入多个数组,就可以将索引存储在帮助程序中,并在上下文更改时增加。我正在使用类似于以下内容的东西:

var data = {
    foo: [{a: 'a', 'b': 'a'}, {'a':'b', 'b':'b'}], 
    i: function indexer() {
        indexer.i = indexer.i || 0;
        if (indexer.last !== this && indexer.last) {                                          
            indexer.i++;
        }   
        indexer.last = this;
        return String(indexer.i);
     }   
};

Mustache.render('{{#foo}}{{a}}{{i}}{{b}}{{i}}{{/foo}}', data);

答案 8 :(得分:1)

(在节点4.4.7中测试,胡须2.2.1。)

如果你想要一个很好的干净功能方法来做,那不涉及全局变量或改变对象本身,使用这个函数;

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

在组装视图时使用它;

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

这种方法的巧妙之处在于它使用旧集合作为原型创建了一组新的“viewmodel”对象。您可以像阅读person.id一样阅读person.firstName。但是,此函数根本不会更改您的人员对象,因此其他代码(可能依赖于不存在的ID属性)将不会受到影响。

算法是O(N),非常好又快。

答案 9 :(得分:0)


推荐的解决方案


我的建议是添加一个index

let data = { "names": [ {"name":"John"}, 
{"name":"Mary"} ] };

// Add an index manually
data = {
  names: [
    { id: 0, name: "John" },
    { id: 1, name: "Mary" }
  ]
};

// Add an index with a loop
data = data.names
  .map(function (name, i) {
    name.id = i;
    return name;
  });

// Nested arrays with indices (parentId, childId)
data = {
  names: [{
    parentId: 0,
    name: "John",
    friends: [{
      childId: 0,
      name: "Mary"
    }]
  }]
};

其他提议的解决方案也不是很干净,并且存在很多如下所述的问题。


有关胡须指数解决方案的问题


@Index

小胡子不支持@Index

索引项

此解决方案运行时间为O(n ^ 2),因此性能不佳。此外,还必须为每个列表手动创建索引。

const data = {
  list: ['a', 'b', 'c'],
  listIndex: function () {
    return data.list.indexOf(this);
  }
};

Mustache.render('{{#list}}{{listIndex}}{{/list}}', data);

全局变量

此解决方案运行时间为O(n),因此此处的性能更好,但也会“污染全局空间”(即,添加到window对象的任何属性,直到浏览器窗口关闭后才收集垃圾),并且必须为每个列表手动创建索引。

const data = {
  list: ['a', 'b', 'c'],
  listIndex: function () {
    return (++window.listIndex || (window.listIndex = 0));
  }
};

Mustache.render('{{#list}}{{listIndex}}{{/list}}', data);

局部变量

此解决方案以O(n)时间运行,不会“污染全局空间”,并且不需要为每个列表手动创建。但是,该解决方案很复杂,不适用于嵌套列表。

const data = {
  listA: ['a', 'b', 'c'],
  index: () => name => (++data[`${name}Index`] || (data[`${name}Index`] = 0))
};

Mustache.render('{{#listA}}{{#index}}listA{{/index}}{{/listA}}', data);

答案 10 :(得分:0)

<script>var index = 1;</script>
{{#names}}
    {{name}} is <script>document.write(index++);</script>
{{/names}}

这很好用。

答案 11 :(得分:0)

我正在使用预先计算功能将'@index'注入到数组中。

const putIndexInArray = array => {
    let putIndex = (value, index) => {
        value['@index'] = index;

        return value;
    };

    if (!Array.isArray(array)) {
        return array;
    }

    return array.map(putIndex);
};

Mustache.render(template, {
    array: putIndexInArray(['Homer', 'Marge', 'Lisa'])
});

然后像这样使用:

{{#array}}
    {{@index}}
{{/array}}