web-dev-qa-db-ja.com

Moustacheで、現在のセクションのインデックスを取得する方法

Mustache を使用し、データを使用しています

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

私の口ひげテンプレートは次のとおりです。

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

私ができることは、配列内の現在の番号のインデックスを取得することです。何かのようなもの:

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

それを印刷してもらう

John is 1
Mary is 2

口ひげでこれを取得することは可能ですか?またはハンドルバーまたは別の拡張機能を使用して?

55

参考までに、この機能は、Mustacheとの互換性を持つHandlebarsに組み込まれています。

つかいます {{@index}}

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

ジョンは0

メアリーは1

31
Kim

これは私が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}}
48
dave

Handlebars.jsでは、ヘルパー関数を使用してこれを実現できます。 (実際、ここでハンドルバーについて言及されている利点の1つは、 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);

(これにより、テンプレートを2回正しくレンダリングできます。)

13
mattsh

オブジェクトのリストで次のループを実行できます。

このソリューションには次の利点があります。

  • モデルデータを変更しません
  • インデックスには複数回アクセスできます

コード:

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

説明:

変数名の代わりに関数が使用されるため、データは変更されません。クロージャーは、ループの最後のiの値の代わりに、ループ内で関数が作成されたときに 'i'の値を返すために使用されます。

7
Chris Dutrow

Moustacheのより良いアプローチは、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}}

6
Pierre

ここで素晴らしいヘルパー:

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

4
lukemh

これの主なユースケースは、アイテムに「アクティブな」クラスを配置する機能でした。 「等しい」ヘルパーと「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'
1
Guy Bedford

(ノード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)なので、素晴らしく高速です。

1
Steve Cooper

JSON文字列の出力を制御できる場合は、これを試してください。

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

そのため、JSON文字列を作成するときに、各オブジェクトの別のプロパティとしてインデックスを追加します。

1
sciritai

複数の配列に移動していない限り、ヘルパーにインデックスを保存し、コンテキストが変更されたときにインクリメントすることができます。私は次のようなものを使用しています:

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

推奨ソリューション


私の推奨は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

Moustacheは@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) timeで実行され、「グローバルスペースを汚染する」ことはなく、リストごとに手動で作成する必要はありません。ただし、ソリューションは複雑で機能しません。ネストされたリストにも適しています。

const data = {
  listA: ['a', 'b', 'c'],
  index: () => name => (++data[`${name}Index`] || (data[`${name}Index`] = 0))
};

Mustache.render('{{#listA}}{{#index}}listA{{/index}}{{/listA}}', data);
0
tfmontague
<script>var index = 1;</script>
{{#names}}
    {{name}} is <script>document.write(index++);</script>
{{/names}}

これは完璧に機能します。

0
Sagar Dolui