# ブロックヘルパー
ブロックヘルパーを使用すると、新しいコンテキストで渡されたブロックを呼び出すことができるカスタムイテレーターやその他の機能を定義できます。
# 基本ブロック
デモンストレーションのために、ヘルパーが存在しないかのようにブロックを呼び出すブロックヘルパーを定義してみましょう。
noop
ヘルパー(「no operation」の略)は、オプションハッシュを受け取ります。このオプションハッシュには、通常のコンパイル済みHandlebarsテンプレートのように動作する関数(options.fn
)が含まれています。具体的には、この関数はコンテキストを受け取り、文字列を返します。
Handlebars.registerHelper("noop", function(options) {
return options.fn(this);
});
Handlebarsは常に現在のコンテキストをthis
としてヘルパーを呼び出すため、this
を使用してブロックを呼び出し、現在のコンテキストでブロックを評価できます。
このように定義されたヘルパーは、コンテキストで定義されたフィールドよりも優先されます。ヘルパーによってマスクされているフィールドにアクセスするには、パス参照を使用できます。上記の例では、コンテキストオブジェクト上のnoop
という名前のフィールドは、次のように参照されます。
# 基本ブロックのバリエーション
構文をより明確にするために、ラップされたテキストにマークアップを追加する別のブロックヘルパーを定義してみましょう。
boldヘルパーは、テキストを太字にするマークアップを追加します。前と同様に、この関数は入力としてコンテキストを受け取り、文字列を返します。
Handlebars.registerHelper("bold", function(options) {
return new Handlebars.SafeString('<div class="mybold">' + options.fn(this) + "</div>");
});
# with
ヘルパー
with
ヘルパーは、パラメーターをヘルパーに渡す方法を示しています。ヘルパーがパラメーターを指定して呼び出されると、テンプレートから渡されたコンテキストを使用して呼び出されます。
JSONオブジェクトのセクションに深くネストされたプロパティが含まれていて、親の名前を繰り返したくない場合、このようなヘルパーが役立つ場合があります。上記のテンプレートは、次のようなJSONで役立ちます。
{
title: "First Post",
story: {
intro: "Before the jump",
body: "After the jump"
}
}
このようなヘルパーの実装は、noop
ヘルパーの実装と非常によく似ています。ヘルパーはパラメーターを受け取ることができ、パラメーターは{{mustache}}
ブロック内で直接使用される式と同じように評価されます。
Handlebars.registerHelper("with", function(context, options) {
return options.fn(context);
});
パラメーターは、渡された順序でヘルパーに渡され、その後にオプションハッシュが続きます。
# 単純なイテレーター
ブロックヘルパーの一般的なユースケースは、カスタムイテレーターを定義することです。実際、Handlebarsのすべての組み込みヘルパーは、通常のHandlebarsブロックヘルパーとして定義されています。組み込みのeach
ヘルパーの動作を見てみましょう。
この場合、comments配列内の各要素に対して、each
に渡されたブロックを1回呼び出す必要があります。
Handlebars.registerHelper("each", function(context, options) {
var ret = "";
for (var i = 0, j = context.length; i < j; i++) {
ret = ret + options.fn(context[i]);
}
return ret;
});
この場合、渡されたパラメーター内のアイテムを反復処理し、各アイテムを使用してブロックを1回呼び出します。反復処理中に、文字列の結果を作成し、それを返します。
このパターンを使用して、より高度なイテレーターを実装できます。たとえば、<ul>
ラッパーを作成し、各結果の要素を<li>
でラップするイテレーターを作成してみましょう。
このテンプレートは、コンテキストとして次のようなものを使い評価します。
{
nav: [
{ url: "http://www.yehudakatz.com", title: "Katz Got Your Tongue" },
{ url: "http://www.sproutcore.com/block", title: "SproutCore Blog" }
];
}
このヘルパーは元のeach
ヘルパーに似ています。
Handlebars.registerHelper("list", function(context, options) {
var ret = "<ul>";
for (var i = 0, j = context.length; i < j; i++) {
ret = ret + "<li>" + options.fn(context[i]) + "</li>";
}
return ret + "</ul>";
});
underscore.jsやSproutCoreのランタイムライブラリなどのライブラリを使用すると、少し見栄えが良くなります。たとえば、SproutCoreのランタイムライブラリを使用した例を以下に示します。
Handlebars.registerHelper("list", function(context, options) {
return (
"<ul>" +
context
.map(function(item) {
return "<li>" + options.fn(item) + "</li>";
})
.join("\n") +
"</ul>"
);
});
# 条件分岐
ブロックヘルパーのもう1つの一般的なユースケースは、条件文を評価することです。イテレーターと同様に、Handlebarsの組み込みif
とunless
制御構造は、通常のHandlebarsヘルパーとして実装されています。
制御構造は通常、現在のコンテキストを変更せず、代わりに変数に基づいてブロックを呼び出すかどうかを決定します。
Handlebars.registerHelper("if", function(conditional, options) {
if (conditional) {
return options.fn(this);
}
});
条件式を作成する際には、条件式がfalseと評価された場合にヘルパーが挿入するHTMLブロックをテンプレートで提供できるようにすることがよくあります。Handlebarsはこの問題を、ブロックヘルパーに汎用的なelse
機能を提供することで解決します。
Handlebarsは、options.inverse
としてelse
フラグメントのブロックを提供します。elseフラグメントの存在を確認する必要はありません。Handlebarsは自動的にそれを検出し、「noop」関数を登録します。
Handlebars.registerHelper("if", function(conditional, options) {
if (conditional) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
Handlebarsは、オプションハッシュのプロパティとしてアタッチすることで、ブロックヘルパーに追加のメタデータを提供します。詳細については、読み進めてください。
条件式は、後続のヘルパー呼び出しをelseマスタッシュに含めることで連鎖させることもできます。
後続の呼び出しで同じヘルパーを使用する必要はありません。他のヘルパーと同様に、else部分でunlessヘルパーを使用できます。ヘルパーの値が異なる場合は、終了マスタッシュが開始ヘルパー名と一致する必要があります。
# ハッシュ引数
通常のヘルパーと同様に、ブロックヘルパーはオプションのハッシュを最後の引数として受け取ることができます。list
ヘルパーを再訪し、作成する<ul>
要素に任意の数のオプション属性を追加できるようにしてみましょう。
Handlebarsは、最終的なハッシュをoptions.hash
として提供します。これにより、可変数のパラメーターを受け入れると同時に、オプションのハッシュも受け入れることが容易になります。テンプレートがハッシュ引数を提供しない場合、Handlebarsは自動的に空のオブジェクト({}
)を渡すため、ハッシュ引数の存在を確認する必要はありません。
Handlebars.registerHelper("list", function(context, options) {
var attrs = Object.keys(options.hash)
.map(function(key) {
return key + '="' + options.hash[key] + '"';
})
.join(" ");
return (
"<ul " +
attrs +
">" +
context
.map(function(item) {
return "<li>" + options.fn(item) + "</li>";
})
.join("\n") +
"</ul>"
);
});
ハッシュ引数は、位置引数によって発生する複雑さなしに、ブロックヘルパーに多くのオプションのパラメーターを提供するための強力な方法です。
ブロックヘルパーは、プライベート変数を子テンプレートに挿入することもできます。これは、元のコンテキストデータにない追加情報を追加する場合に役立ちます。
たとえば、リストを反復処理するときに、現在のインデックスをプライベート変数として提供できます。
Handlebars.registerHelper("list", function(context, options) {
var out = "<ul>",
data;
if (options.data) {
data = Handlebars.createFrame(options.data);
}
for (var i = 0; i < context.length; i++) {
if (data) {
data.index = i;
}
out += "<li>" + options.fn(context[i], { data: data }) + "</li>";
}
out += "</ul>";
return out;
});
data
オプションを介して提供されるプライベート変数は、すべての子スコープで使用できます。
親スコープで定義されたプライベート変数は、パスのクエリを介してアクセスできます。親イテレーターのindex
フィールドにアクセスするには、@../index
を使用できます。
各ヘルパーで新しいデータフレームを作成し、独自のデータを設定してください。そうしないと、ダウンストリームのヘルパーがアップストリームの変数を予期せず変更する可能性があります。
また、既存のデータオブジェクトを操作しようとする前に、data
フィールドが定義されていることを確認してください。プライベート変数の動作は条件付きでコンパイルされるため、一部のテンプレートではこのフィールドが作成されない場合があります。
# ブロックパラメーター
Handlebars 3.0の新機能として、対応するヘルパーから名前付きパラメーターを受け取ることができます。
この特定の例では、user
は現在のコンテキストと同じ値を持ち、userId
は反復処理のインデックス値を持ちます。
これにより、ネストされたヘルパーは、プライベート変数で発生する可能性のある名前の競合を回避できます。
いくつかの組み込みヘルパーはブロックパラメーターをサポートしており、カスタムヘルパーはblockParams
オプションフィールドを介してそれらを指定できます。
ヘルパーは、options.fn.blockParams
フィールドを介してテンプレートによって参照されるブロックパラメーターの数を判断できます。これは整数カウントです。この値は、子テンプレートで参照できるブロックパラメーターの数を表します。このカウントを超えるパラメーターは参照されることはなく、必要に応じてヘルパーによって安全に省略できます。これはオプションであり、テンプレートに渡される追加のパラメーターは黙って無視されます。
# Rawブロック
未処理のマスタッシュブロックを処理する必要があるテンプレートには、Rawブロックを使用できます。
は、コンテンツを解釈せずにraw-helperヘルパーを実行します。
Handlebars.registerHelper("raw-helper", function(options) {
return options.fn();
});
は、次のようにレンダリングされます。
{{bar}}