CLOVER🍀

That was when it all began.

Handlebars.scalaのHelperを使ってみる

先ほど、Handlebars.scalaについてのエントリを書きました。

Handlebars.scalaを使ってみる
http://d.hatena.ne.jp/Kazuhira/20140920/1411200907

この続きとして、Handlerbars.scalaのHelperを試してみたいと思います。

Helpers
https://github.com/mwunsch/handlebars.scala#helpers

Handlebars.scalaでは使えないもの(Helperの数が少ない)、機能もありますが、Handlebars本家のドキュメントも参考に。

Built-In Helpers
http://handlebarsjs.com/builtin_helpers.html

Handlebars.scalaには、組み込みのHelperの説明がほぼないので、詳しくはソースを読みましょう!になりますね…。

Helperって、ブロック({{# }})との違いが最初よくわからなかったんですが、機能を追加できる仕組み(JSPでいうTaglib的な?)イメージでいいんですかねー。

if helper

{{#if }}の引数として、trueかfalseでボディが評価されるかが変わります。

    val template = """|{{#if this}}
                      |trueなら、ここが表示
                      |{{/if}}""".stripMargin

    val handlebars = Handlebars(template)
    val result = handlebars(true)

    println(result)

結果。

trueなら、ここが表示

{{#if }}に渡した値がfalseならボディは評価されませんが、elseでfalse時に評価される場合の定義をすることができます。

    val template = """|{{#if this}}
                      |trueなら、ここが表示
                      |{{else}}
                      |falseなら、ここが表示
                      |{{/if}}""".stripMargin

    val handlebars = Handlebars(template)
    val result = handlebars(false)

    println(result)

結果。

falseなら、ここが表示

…こう見ると、{{# }}とほぼ変わりませんけど。

each helper

コレクションなどの繰り返し用のHelperです。

    val template = """|People
                      |{{#each people}}
                      |  Person
                      |    name = {{name}}
                      |    age = {{age}}
                      |{{/each}}""".stripMargin

    val handlebars = Handlebars(template)
    val result = handlebars(Map("people" -> List(Person("フグ田サザエ", 24),
                                                 Person("磯野カツオ", 11),
                                                 Person("磯野ワカメ", 9))))

    println(result)

IterableやMapを反復処理することができます。

結果。

People

  Person
    name = フグ田サザエ
    age = 24

  Person
    name = 磯野カツオ
    age = 11

  Person
    name = 磯野ワカメ
    age = 9

本家と違って、elseは動かないみたいです…。

each helperでは、@indexという表記が使え、現在のインデックスを取得できます。

    val template = """|People
                      |{{#each people}}
                      |  Person:{{@index}}
                      |    name = {{name}}
                      |    age = {{age}}
                      |{{/each}}""".stripMargin

    val handlebars = Handlebars(template)
    val result = handlebars(Map("people" -> List(Person("フグ田サザエ", 24),
                                                 Person("磯野カツオ", 11),
                                                 Person("磯野ワカメ", 9))))

    println(result)

結果。

People

  Person:0
    name = フグ田サザエ
    age = 24

  Person:1
    name = 磯野カツオ
    age = 11

  Person:2
    name = 磯野ワカメ
    age = 9

評価したものがMapだった場合は、@keyも使用できるようになります。

    val template = """|Map
                      |{{#each map}}
                      |  {{@key}}:{{this}}:{{@index}}
                      |{{/each}}
                      |
                      |Map[key1] => {{map.key1}} or {{map/key1}}""".stripMargin

    val handlebars = Handlebars(template)
    val result = handlebars(Map("map" -> Map("key1" -> "value1",
                                             "key2" -> "value3",
                                             "key3" -> "value3")))

    println(result)

ちなみに、Mapの値も{{Map名.キー}}や{{Map名/キー}}で取得できます。

結果。

Map

  key1:value1:0

  key2:value3:1

  key3:value3:2


Map[key1] => value1 or value1

with helper

スコープをその変数にして、ひとつ参照階層を下げることができます。

    val template = """|{{#with person}}
                      |  Person
                      |    name = {{name}}
                      |    age = {{age}}
                      |{{/with}}""".stripMargin

    val handlebars = Handlebars(template)
    val result = handlebars(Map("person" -> Person("磯野カツオ", 11)))

    println(result)

eachや{{# }}と迷いますが、JavaScriptのwithみたいな感じ?

結果。

  Person
    name = 磯野カツオ
    age = 11

Helperを自作する

最後に、自分でHelperを作ってみましょう。

与えられた引数に「Hello」と「!」を付けて返す、簡単なものを作成してみます。
*すごいいい加減ですが…

import com.gilt.handlebars.scala.helper.{Helper, HelperOptions}
import com.gilt.handlebars.scala.binding.{Binding, BindingFactory}

class MyHelper[T] extends Helper[T] {
  override def apply(binding: Binding[T], options: HelperOptions[T])(implicit contextFactory: BindingFactory[T]): String = {
    val arg = options.argument(0).getOrElse("MyHelper".asInstanceOf[T])
    s"Hello $arg!"
  }
}

Helperは、まずHelperトレイトを継承して、applyメソッドをオーバーライドして定義します。Helperへの引数は、HelperOptionsとして渡ってきます。

ここでは、第1引数、なければ「MyHelper」をデフォルト値として「Hello」「!」をくっつけて返します。

applyメソッドの戻り値はStringである必要があります。

使ってみます。

    val template = "{{#my word}}{{/my}}".stripMargin

    val handlebars = Handlebars(template)
    val result = handlebars(Map("word" -> "Handlebars.scala"),
                            helpers = Map("my" -> new MyHelper))

    println(result)

Handlebars#applyメソッドのhelpers引数に、自分で作ったHelperをMapとして渡します。

結果。

Hello Handlebars.scala!

ひとまず、こんなところでしょうか。