我正在使用Mustache并使用数据
{ "names": [ {"name":"John"}, {"name":"Mary"} ] }
我的小胡子模板是:
{{#names}}
{{name}}
{{/names}}
我希望能够获得数组中当前数字的索引。类似的东西:
{{#names}}
{{name}} is {{index}}
{{/names}}
并将其打印出来
John is 1
Mary is 2
是否有可能通过Moustache获得此功能?或使用把手或其他扩展名?
答案 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;
});
答案 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}}