はじめに
Mustache.jsは便利だが、リカーシブなテンプレートを作成したい場合にはクセがあり分かりにくい。
やり方をググってもなかなか出てこないので、やり方をメモっておく。
Mustacheテンプレート
コードは以下のように作っておく。
> attribute
の部分が、Partialsという部品呼び出しの記法だ。これ自体は、メインのテンプレートファイルが大きくなりすぎる場合に部品を切り出すための記法であるが、これを応用することでリカーシブに呼ぶことができる。
main.mustache
{{#attributes}}
{{> attribute}}
{{/attributes}}
attribute.mustache
{{attributeName}}
{{#attributeValue}}
{{> attribute}}
{{/attributeValue}}
Typescriptのサンプル① 短項目の場合
上記の機能を呼び出すタイプスクリプトは以下のように書ける。
index.ts
import * as Mustache from 'mustache';
import { readFileSync } from 'fs';
const path = require('path');
type templateDataAttribute = {
attributeName: string;
attributeValue: null | (string | templateDataAttribute)[];
}
type templateData = {
attributes: templateDataAttribute[];
}
const main = () => {
const template: string = readFileSync(path.join(__dirname, 'mustache/main.mustache'), 'utf-8');
const partial: string = readFileSync(path.join(__dirname, 'mustache/attribute.mustache'), 'utf-8');
{
const templateData: templateData = {
attributes: [{
attributeName: 'hoge',
attributeValue: null,
}],
};
console.log('--- case 1 ---');
console.log(Mustache.render(template, templateData, { attribute: partial }));
}
};
main();
上記を実行すると、以下のような出力が得られる。
--- case 1 ---
hoge
nullで終端することで、attribute.mustache
側の{{#attributeValue}}
が呼ばれなくなるという仕組みだ。
Typescriptのサンプル① 1階層ネストでのリカーシブコール
上記のindex.ts
のmain()関数に以下のコードを書き加える。
index.ts
// 前略
{
const templateData: templateData = {
attributes: [{
attributeName: 'hoge',
attributeValue: [{
attributeName: 'hige',
attributeValue: null,
}],
}],
};
console.log('--- case 2 ---');
console.log(Mustache.render(template, templateData, { attribute: partial }));
}
// 以下略
上記を実行すると、以下のような出力が得られる。
--- case 2 ---
hoge
hige
ここまでくれば、あとはお手の物だろう。
Typescriptのサンプル③ 2階層ネストでのリカーシブコール
上記のindex.ts
のmain()関数に以下のコードを書き加える。
index.ts
// 前略
{
const templateData: templateData = {
attributes: [{
attributeName: 'hoge',
attributeValue: [{
attributeName: 'hige',
attributeValue: [{
attributeName: 'hage',
attributeValue: null,
}],
}],
}],
};
console.log('--- case 3 ---');
console.log(Mustache.render(template, templateData, { attribute: partial }));
}
// 以下略
上記を実行すると、以下のような出力が得られる。
--- case 3 ---
hoge
hige
hage
バッチリ2段階のネストもいける!
ループや条件分岐も組み合わせることで、複雑な構造もMustacheのみで書けそうだ!