Ответ 1
Для справки, эта функциональность теперь встроена в Handlebars, которая имеет совместимость с Mustache.
Используйте {{@index}}
{{#names}}
{{name}} is {{@index}}
{{/names}}
Джон = 0
Мэри 1
Я использую Mustache и используя данные
{ "names": [ {"name":"John"}, {"name":"Mary"} ] }
Мой шаблон усы:
{{#names}}
{{name}}
{{/names}}
То, что я хочу сделать, - это получить индекс текущего числа в массиве. Что-то вроде:
{{#names}}
{{name}} is {{index}}
{{/names}}
и распечатать его
John is 1
Mary is 2
Можно ли получить это с Усы? или с ручками или другим расширением?
Для справки, эта функциональность теперь встроена в Handlebars, которая имеет совместимость с Mustache.
Используйте {{@index}}
{{#names}}
{{name}} is {{@index}}
{{/names}}
Джон = 0
Мэри 1
Вот как я это делаю в 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}}
В 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 должен получать reset каждый раз при визуализации шаблона, поэтому для этого вы можете иметь помощник reset в начале списка. Таким образом, полный код выглядит следующим образом:
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);
(Это может корректно отобразить шаблон дважды.)
Вы можете запустить следующий цикл в списке объектов.
Это решение имеет следующие преимущества:
Код:
for( var i=0; i< the_list.length; i++) {
the_list[i].idx = (function(in_i){return in_i+1;})(i);
}
Объяснение:
Вместо имени переменной используется функция, поэтому данные не мутируются. Закрытие используется для возврата значения "i" во время создания функции в цикле вместо значения я в конце цикла.
Великий помощник здесь:
// {{#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
Лучшим подходом для 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}}
.
Если вы можете управлять выходом строки JSON, попробуйте это.
{ "names": [ {"name":"John", "index":"1"}, {"name":"Mary", "index":"2"} ] }
Поэтому, когда вы создаете строку JSON, добавьте индекс как другое свойство для каждого объекта.
Моим основным прецедентом для этого была возможность размещения "активного" класса на элементах. Я объединился с объединением помощника "equals" с помощником "index_for_each", но это было слишком сложно.
Вместо этого я придумал следующий базовый "активный" помощник. Это очень специфично, но является таким распространенным вариантом использования для любого сценария меню/выбора:
Использование:
<ul>
{{{active 2}}}
{{#each menuItem}}
<li class="{{{active}}}">{{this}}</li>
{{/each}}
</div>
Сделал бы 3-й пункт меню "class=" активным ".
Помощник здесь (это версия CoffeeScript):
active = 0
cur = 0
handlebars.registerHelper 'active', (item, context) ->
if arguments.length == 2
active = item
cur = 0
''
else if cur++ == active
'active'
Пока вы не путешествуете в несколько массивов, вы можете хранить индекс в помощнике и просто увеличивать его при изменении контекста. Я использую что-то похожее на следующее:
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);
(Протестировано в node 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), такой приятный и быстрый.