Mustache を使用し、データを使用しています
{ "names": [ {"name":"John"}, {"name":"Mary"} ] }
私の口ひげテンプレートは次のとおりです。
{{#names}}
{{name}}
{{/names}}
私ができることは、配列内の現在の番号のインデックスを取得することです。何かのようなもの:
{{#names}}
{{name}} is {{index}}
{{/names}}
それを印刷してもらう
John is 1
Mary is 2
口ひげでこれを取得することは可能ですか?またはハンドルバーまたは別の拡張機能を使用して?
参考までに、この機能は、Mustacheとの互換性を持つHandlebarsに組み込まれています。
つかいます {{@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では、ヘルパー関数を使用してこれを実現できます。 (実際、ここでハンドルバーについて言及されている利点の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回正しくレンダリングできます。)
オブジェクトのリストで次のループを実行できます。
このソリューションには次の利点があります。
コード:
for( var i=0; i< the_list.length; i++) {
the_list[i].idx = (function(in_i){return in_i+1;})(i);
}
説明:
変数名の代わりに関数が使用されるため、データは変更されません。クロージャーは、ループの最後のiの値の代わりに、ループ内で関数が作成されたときに 'i'の値を返すために使用されます。
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}}
。
ここで素晴らしいヘルパー:
// {{#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;
});
これの主なユースケースは、アイテムに「アクティブな」クラスを配置する機能でした。 「等しい」ヘルパーと「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'
(ノード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)なので、素晴らしく高速です。
JSON文字列の出力を制御できる場合は、これを試してください。
{ "names": [ {"name":"John", "index":"1"}, {"name":"Mary", "index":"2"} ] }
そのため、JSON文字列を作成するときに、各オブジェクトの別のプロパティとしてインデックスを追加します。
複数の配列に移動していない限り、ヘルパーにインデックスを保存し、コンテキストが変更されたときにインクリメントすることができます。私は次のようなものを使用しています:
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);
私の推奨は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);
<script>var index = 1;</script>
{{#names}}
{{name}} is <script>document.write(index++);</script>
{{/names}}
これは完璧に機能します。