59

我正在使用Mustache并使用数据

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

我的胡子模板是:

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

我想要做的是获取数组中当前数字的索引。就像是:

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

并打印出来

John is 1
Mary is 2

用 Mustache 可以得到这个吗?或使用把手或其他扩展?

4

13 回答 13

51

这就是我在 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}}
于 2012-04-18T11:18:12.733 回答
31

作为参考,此功能现在已内置到与 Mustache 兼容的 Handlebars 中。

采用{{@index}}

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

约翰是 0

玛丽是 1

于 2012-11-25T21:16:13.920 回答
12

在 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);

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

于 2011-02-22T02:24:57.410 回答
7

您可以在对象列表上运行以下循环。

该解决方案具有以下优点:

  • 不改变模型数据
  • 索引可以被多次访问

代码:

for( var i=0; i< the_list.length; i++) {
    the_list[i].idx = (function(in_i){return in_i+1;})(i);
}

解释:

使用函数而不是变量名,因此数据不会发生变异。闭包用于在循环中创建函数时返回 'i' 的值,而不是在循环结束时返回 i 的值。

于 2012-09-23T04:57:46.230 回答
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}}.

于 2014-10-26T15:43:23.160 回答
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

于 2011-11-05T20:10:27.807 回答
1

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

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

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

于 2011-02-16T20:14:45.457 回答
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);
于 2013-06-29T00:23:15.283 回答
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),非常好和快。

于 2016-07-21T09:25:20.693 回答
1

推荐解决方案


我的建议是添加一个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"
    }]
  }]
};

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


建议的 Mustache 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);
于 2018-11-29T04:12:15.000 回答
0

我的主要用例是能够在项目上放置一个“活动”类。我将“equals”助手与“index_for_each”助手组合在一起,但它太复杂了。

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

用法:

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

将使第三个菜单项具有“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'
于 2012-09-06T18:47:46.877 回答
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}}
于 2019-11-08T20:49:40.903 回答
-1
<script>var index = 1;</script>
{{#names}}
    {{name}} is <script>document.write(index++);</script>
{{/names}}

这工作得很好。

于 2018-12-28T13:41:27.920 回答