便利ですよね、Handlebars。
でも日本語の記事が案外なかったり、公式のドキュメントがいまいちアレだったりするので、自分用にまとめます。
それがどこかで誰かの役に立てば、それはそれで幸いです。
基本的な使い方
インストールとかは割愛。
だって読み込むだけやし・・・。
で、そこからは
- テンプレート・ひな形となるHTMLを用意
- そのHTMLをHandlebarsに食わせる
- 動的にはめ込む値をつっこむ
基本的にはこの3ステップです。
Html
<!-- 略 --> <script id="input" type="text/x-handlebars-template"> <section class="inner"> <h1 class="header">{{title}}</h1> <div class="box"> <img src="{{img.url}}" alt="{{img.alt}}" /> <p class="flex">{{text}}</p> </div> </section> </script> <div id="output"></div> <!-- 略 -->
- type="text/x-handlebars-template"としたscript要素でテンプレートをくくる
- 最終的に出力したい要素を用意する
- 変数として動的にしたい部分を、{{ }} でくくって名前をつける
typeはscriptとして認識されなければ何でも良いです。
JavaScript
// Handlebars.jsを読み込む // jQueryとかライブラリを読み込む var values = { title: 'Hello Handlebars!', img: { url: 'http://example.com', alt: 'Something..' }, text: 'My first Handlebars!' }; var template = Handlebars.compile($('#input').html()); $('#output').html(template(values));
- 変数オブジェクトを用意する
- Handlebarsでテンプレートをコンパイルしておく
- 最後に出力!
読み込ませる変数は、自分で用意することもあればAPI等からjsonで取得することもあるでしょうけど、どっちでもやることは同じです。
↑のサンプルのように構えておけば、イベントで再度値を出力するときも楽だと思います。
そうすると
<div id="output"> <section class="inner"> <h1 class="header">Hello Handlebars!</h1> <div class="box"> <img src="http://example.com" alt="Something.."> <p class="flex">My first Handlebars!</p> </div> </section> </div>
ってなHtmlが出力されるってわけですね。
img.urlとしてるのは、読み込ませたvaluesのコンテキストが云々かんぬん・・なのですが、だいたい雰囲気でわかるかと。
.じゃなくて/とかでも上がったり下がったりできるらしい。
ループ
TwitterのAPIなどjsonを山ほど取得して、だーっと表示したいケース。
Html
<!-- 略 --> <script id="input" type="text/x-handlebars-template"> <ul> {{#each tweets}} <li>{{tweet}} by {{user}}</li> {{/each}} </ul> </script> <div id="output"></div> <!-- 略 -->
- 繰り返しには#eachと繰り返したいオブジェクト
- 終わりを表す/eachをお忘れなく
JavaScript
// Handlebars.jsを読み込む // jQueryとかライブラリを読み込む var values = { tweets: [ { user: 'Yuji', tweet: 'I\'m hungry...' }, { user: 'Koji', tweet: 'I\'m sleepy...' }, { user: 'Ryuji', tweet: 'I\'m xxxxxx...' } ] }; var template = Handlebars.compile($('#input').html()); $('#output').html(template(values));
- valuesではなく直接tweetsをつっこんでも良いです。(その場合は #each this
そうすると
<div id="output"> <ul> <li>I'm hungry... by Yuji</li> <li>I'm sleepy... by Koji</li> <li>I'm xxxxxx... by Ryuji</li> </ul> </div>
まぁこの場合はoutput先をulにしておくのが良いかも。
あと、
$('#output').html(template(values)); // 別にhtml()じゃなくて // $('#output').append(template(values)); // 別にappend()とかでももちろんOK
ここはHandlebars関係ないですから。
Ajaxで随時追加・・系はこうなりますよね。
if文
ここでちょっとHandlebarsについて。
ロジックレステンプレート
というように、ロジックを持たないのがこのライブラリの特徴ですが、徹底的です。(表向きには
値が0以上だったら・・のif文くらい書けると思いがちですが、それもできません。(表向きには
サーバー側でロジックは作って、出力する値も確定させてそれをそのままクライアント側に表示する、そのためのテンプレートエンジン。
ちょっとくらいクライアント側でも・・と思うなら、違う仕組みを使ったほうが便利かもですよ。
それらしい例と、if文の限界を・・。
Html
<!-- 略 --> <script id="input" type="text/x-handlebars-template"> <section> <h2>パーティ状況</h2> <dl> {{#each myParty}} <dt>しょくぎょう</dt> <dd>{{name}}</dd> <dt>HP</dt> <dd>{{current_hp}}/{{max_hp}}</dd> <dt>じょうたい</dt> <dd>{{#if isLive}}げんき{{else}}ひんし{{/if}}</dd> <!-- ほんとはこんなことしたい(けどできない --> <!-- <dd>{{#if current_hp > 80}} すごくげんき {{elseif current_hp > 0}} げんき {{else}} ひんし {{/if}}</dd> --> {{/each}} </dl> </section> </script> <div id="output"></div> <!-- 略 -->
できません。
JavaScript
// Handlebars.jsを読み込む // jQueryとかライブラリを読み込む var values = { myParty: [ { name: 'ゆうしゃ', current_hp: 20, max_hp: 100, isLive: true }, { name: 'けんじゃ', current_hp: 0, max_hp: 100, isLive: false } ] }; var template = Handlebars.compile($('#input').html()); $('#output').html(template(values));
そうすると
<div id="output"> <section> <h2>パーティ状況</h2> <dl> <dt>しょくぎょう</dt> <dd>ゆうしゃ</dd> <dt>HP</dt> <dd>20/100</dd> <dt>じょうたい</dt> <dd>げんき</dd> <dt>しょくぎょう</dt> <dd>けんじゃ</dd> <dt>HP</dt> <dd>0/100</dd> <dt>じょうたい</dt> <dd>ひんし</dd> </dl> </section> </div>
- true/falseもしくは値の有無程度のif文は書けます(無理やり
- HP20以上あったら・・とかは書けません。
ロジックレスですからー。
そこで最後に覚えておきたいのが、ヘルパー関数です。
ヘルパー関数
Handlebarsには関数を定義して、結果を出力する機能があります。
何を隠そうeachもifも、それで定義されてるんです。
オリジナルな関数を作れるということは、なんでもできるということです。
たとえそれがHandlebarsの思想に背くものであったとしても・・・。
↑でやりたかったことを実現してみる
Html
<!-- 略 --> <script id="input" type="text/x-handlebars-template"> <section> <h2>パーティ状況</h2> <dl> {{#each myParty}} <dt>しょくぎょう</dt> <dd>{{name}}</dd> <dt>HP</dt> <dd>{{current_hp}}/{{max_hp}}</dd> <dt>じょうたい</dt> <dd>{{#showCondition current_hp}}{{/showCondition}}</dd> {{/each}} </dl> </section> </script> <div id="output"></div> <!-- 略 -->
ここでは#showConditionという関数を定義することにします。
JavaScript
// Handlebars.jsを読み込む // jQueryとかライブラリを読み込む var values = { myParty: [ { name: 'ゆうしゃ', current_hp: 90, max_hp: 100, isLive: true }, { name: 'けんじゃ', current_hp: 0, max_hp: 100, isLive: false }, { name: 'ぶとうか', current_hp: 30, max_hp: 100, isLive: true } ] }; var template = Handlebars.compile($('#input').html()); Handlebars.registerHelper('showCondition', function(cond, opt) { var condition = ''; if (cond > 80) { condition = 'すごくげんき'; } else if (cond > 10) { condition = 'げんき'; } else { condition = 'ひんし'; } return condition; }); $('#output').html(template(values));
見ての通り、showConditionはcurrent_hpを引数にとり、じょうたいを返します。
そうすると
<div id="output"> <section> <h2>パーティ状況</h2> <dl> <dt>しょくぎょう</dt> <dd>ゆうしゃ</dd> <dt>HP</dt> <dd>90/100</dd> <dt>じょうたい</dt> <dd>すごくげんき</dd> <dt>しょくぎょう</dt> <dd>けんじゃ</dd> <dt>HP</dt> <dd>0/100</dd> <dt>じょうたい</dt> <dd>ひんし</dd> <dt>しょくぎょう</dt> <dd>ぶとうか</dd> <dt>HP</dt> <dd>30/100</dd> <dt>じょうたい</dt> <dd>げんき</dd> </dl> </section> </div>
という具合です。
もちろんこんな風にHandlebarsに背いてガチガチに書くもよしですが、こんな例も載せておきます。
Html
<p> {{#isGreaterThanZero user.current_hp}} まだ戦えるよ! {{else}} HPがやばい! {{/isGreaterThanZero}} </p>
JavaScript
Handlebars.registerHelper('isGreaterThanZero', function(cond, opt) { console.log(); if(cond > 0) { return opt.fn(this); // まだ戦えるよ! のこと } else { return opt.inverse(this); // HPがやばい! のこと } });
こっちのがまだHandlebarsっぽいw
registerHelperの引数
Handlebars.registerHelper('someFunc', function(cond1, cond2, condN, opt) { // ここで処理 });
引数はいくらでも追加できます。
そして引数とは別に、オプションとなる引数が使えます。
inverseとかhashとかfnとか。
おわりに
だいたいのことには触れたと思いますが、足りない部分は公式へ。
参考:Handlebars.js: Minimal Templating on Steroids
参考:Try Handlebars.js in your web browser