В усах, как получить индекс текущего раздела


Я использую ус и, используя данные

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

мой шаблон усы:

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

то, что я хочу быть в состоянии сделать это, чтобы получить индекс текущего числа в массив. Что-то вроде:

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

и пусть он распечатает

John is 1
Mary is 2

можно ли получить это с усами? или с рулями или другим расширением?

10 51

10 ответов:

Для справки, эта функция теперь встроена в руль, который имеет совместимость с усами.

использовать {{@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}}

в руле.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);

(это может правильно отобразить шаблон дважды.)

вы можете запустить следующий цикл в списке объектов.

данное решение имеет следующие преимущества:

  • не изменяет данные модели
  • индекс может быть получен несколько раз

код:

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

объяснение:

вместо имени переменной используется функция, поэтому данные не мутируют. Закрытие используется для возврата значения 'i' в то время функция создается в цикле вместо значения i в конце цикла.

лучший подход для усов будет использовать функцию, которая возвращает индекс с помощью 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}}.

большой помощник здесь:

// {{#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

Если вы можете управлять выводом строки 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= 'active'".

в помощник здесь (это версия 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);

(проверено в узле 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. Однако, эта функция не изменяет ваши объекты people вообще, поэтому другой код (который мог бы полагаться на свойство ID, которого там нет) не будет затронут.

алгоритм O (N), так красиво и быстро.