【问题标题】:In Mustache, How to get the index of the current Section在 Mustache 中,如何获取当前 Section 的索引
【发布时间】:2011-06-28 15:13:52
【问题描述】:

我正在使用Mustache 并使用数据

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

我的小胡子模板是:

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

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

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

打印出来

John is 1
Mary is 2

可以用 Mustache 获得这个吗?还是使用 Handlebars 或其他扩展?

【问题讨论】:

  • 在 O(n) 时间内运行...Mustache.render('{{#list}}{{index}}{{/list}}', { list: ['a', 'b', 'c' ], index: () => (++this.i || (this.i = 0)) });

标签: templates mustache


【解决方案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}}

【讨论】:

  • 这个问题的一个问题是你必须记住只能得到一次idx,否则它会改变当前对象。在我们的一些模板中,我们在多个位置使用当前对象的索引。
  • 很好的答案,我为多个 {{idx}} 添加了解决方案
  • multiple {{index}} 的解决方案可以在Index of an array element in Mustache.js @oyatek 上找到
  • 我在模板视图中使用了一个函数,该函数使用indexOf 返回索引,查看my answer 了解更多详细信息。
【解决方案2】:

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

使用{{@index}}

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

约翰是 0

玛丽 1 岁

【讨论】:

  • 车把与小胡子兼容是一种常见的误解。车把根本不兼容小胡子。它们根本不可互换。只是想澄清一下,因为我试图将一个用于另一个。
  • 只留胡子有什么意思吗?
  • Handlebars 与 mustache 完全不兼容。即使他们在他们的网站上这样说,这也是完全错误的。
  • 这不应该是公认的答案,因为它没有回答标题中的问题。
  • 这是一个糟糕的答案。 Mustache 不是特定于平台的,而 Handlebars 是。
【解决方案3】:

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

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

【讨论】:

  • 这可以工作,虽然它有一个问题,你不能在项目期间在多个地方使用索引。因为每次检索索引时都会增加它。这对模板设计者来说效果不佳,并且会导致错误和问题。此外,让“reset_index”添加了一个逻辑方法来重置索引,我相信这又是无逻辑的模板。我在 Android 版本中看到他们有可用的 {{-index}} 做正确的事情。
【解决方案4】:

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

此方案具有以下优点:

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

代码:

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

说明:

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

【讨论】:

    【解决方案5】:

    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}}

    【讨论】:

    • 另一个问题是这个算法是 O(N^2),这可能会导致中等大小的列表出现问题。每次获取一个项目的索引时,都必须搜索整个列表。
    【解决方案6】:

    这里的好帮手:

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

    【讨论】:

    【解决方案7】:

    推荐解决方案


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

    小胡子不支持@Index

    IndexOf

    这个解决方案在 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);
    

    【讨论】:

      【解决方案8】:

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

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

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

      【讨论】:

      • 有没有办法做到这一点,而不必将索引属性添加到数据中?我得到的数据没有它,我取了它的一个子集。我希望免费获得索引。
      • 不幸的是,似乎没有。您可以在运行模板之前尝试在 JS 中对其进行迭代,然后添加索引。或者看看源代码,看看是否可以添加一些东西来返回当前索引。
      • 好的,我将把它打开几天,看看是否有其他解决方案。否则我会将此标记为正确。
      • 下面有更好的解决方案,您应该更改您选择的答案
      • 您指的是哪种解决方案?许多其他解决方案(除了现在在 Handlebars 中对索引的原生支持)只允许检索一次索引。这不是一个很好的解决方案,尤其是因为我们需要在模板中多次插入索引。
      【解决方案9】:

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

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

      【讨论】:

        【解决方案10】:

        (在节点 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.firstName 一样阅读 person.id。但是,此函数根本不会更改您的人员对象,因此其他代码(可能依赖于不存在的 ID 属性)不会受到影响。

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

        【讨论】:

          【解决方案11】:

          我的主要用例是能够在项目上放置一个“活动”类。我把“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'
          

          【讨论】:

            【解决方案12】:

            我正在使用预计算函数将“@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}}
            

            【讨论】:

              【解决方案13】:
              <script>var index = 1;</script>
              {{#names}}
                  {{name}} is <script>document.write(index++);</script>
              {{/names}}
              

              这很好用。

              【讨论】:

              • 这有点违背了Mustache的目的。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2019-04-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-08-13
              • 2011-11-12
              • 2022-11-21
              相关资源
              最近更新 更多