Play Framework 2.3 For Java ことはじめ #9 FieldConstructor編
Play Framework 2.3 For Java 入門記事一覧
第9回はページャーとかセッションとかやろうと思ったけど、やっぱりフォーム編で話したFieldConstructor
という概念がとても難しかったので、もうちょっと詳しく見ていこうと思います。
FieldConstructor
は@form(){}
の中で、@inputText()
などのフォームのヘルパーを使うと自動的に適用されるというものだってことは分かりました。でも、実際のWebアプリケーションで、フォームの中が全て、同じtypeのフォームばっかり(例えばテキストボックスだけ)みたいなことって絶対ないし、同じhtml構造をとれるわけがないと思います。
例えば、サンプルプロジェクトの一つの、computer-database-javaだと、こんな風に一つのフォームに対して、全て同じtwitterBootstrapInput
のFieldConstructor
が適用されてしまいます。
テンプレートの部品の方のhtml : twitterBootstrapInput.scala.html
@(elements: helper.FieldElements) @*************************************************** * Generate input according twitter bootstrap rules * ***************************************************@ <div class="clearfix @if(elements.hasErrors) {error}"> <label for="@elements.id">@elements.label</label> <div class="input"> @elements.input <span class="help-inline">@elements.infos.mkString(", ")</span> </div> </div>
formを使う側のhtml
@(computerForm: Form[Computer]) @import helper._ @implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) } @main { <h1>Add a computer</h1> @form(routes.Application.save()) { <fieldset> @inputText(computerForm("name"), '_label -> "Computer name", '_help -> "") @inputDate(computerForm("introduced"), '_label -> "Introduced date", '_help -> "") @inputDate(computerForm("discontinued"), '_label -> "Discontinued date", '_help -> "") @select( computerForm("company.id"), options(Company.options), '_label -> "Company", '_default -> "-- Choose a company --", '_showConstraints -> false ) </fieldset> <div class="actions"> <input type="submit" value="Create this computer" class="btn primary"> or <a href="@routes.Application.list()" class="btn">Cancel</a> </div> } }
これだと、type="text"
も、type="date"
も、select
も全部同じtwitterBootstrapInput
というFieldConstructor
が適用されちゃいますよね。
同じフォーマットで作るべきだというならそれはそれで正しいんですが、実際のプロジェクトではそうはいかないですよね。
そんなときに、どうやって別々の構造させるのを適用させるべきなのか。前置きが長くなりましたが、今回は、それについて考えてみました。
1. テンプレート部品側で場合分けする
素直に考えれば、テンプレート部品側twitterBootstrapInput.scala.html
で、何かの値によって場合分けすればいいかなと思う訳です。
例えば、@inputText
で渡すパラメータにhtmlの属性として判断するための値を追加したり、渡ってくるinput要素をパースとかすれば、その文字列で出力するタグをif文で分けることはできました。
@if(elements.args.contains('paramA)){ //Do something here }else{ //Do something else }
でも、この方法ってなんかイケてないというか、せっかく型安全をうたってるのに、属性の文字列なんかで処理してしまったらこの素敵なテンプレートエンジンが台無しだなと思うわけです。他の方法があるはずだと。
2. 別々のFieldConstructorを指定するには
formの中の部品を呼び出す側のhtmlでは、FieldConstructorを指定してますが、こんな風に一つしか指定できません。
@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) }
やっぱり、使い方をちゃんと知るには、リファレンスを見るしかないですよね。
そもそも@inputText(...)
のところがどんなことをしているのかってのを確認してみます。
exampleを見ると、
@inputText(field = myForm("name"), args = 'size -> 10, 'placeholder -> "Your name")
と書いてあって、なんかフォームとhtmlの属性以外は渡せないんじゃないかなーという印象を受けます。
でも、Scalaといえばapply
ということでそのメソッドの定義をちゃんと見てみると、、、
def apply(field: Field, args: (Symbol, Any)*)(implicit handler: FieldConstructor, lang: Lang)
ん?
なんか、最初のかっこ(field: Field, args: (Symbol, Any)*)
の後ろに、(implicit handler: FieldConstructor, lang: Lang)
がついてて、そこには、FieldConstructor
の文字が!!
これは、色々なFieldConstructor
が渡せそうだとなる訳です。
そんで、how to create different type fieldconstructor playframework
とかでググってるとやっと分けてるやつが見つかりました。Play Framework 2.0 Templates - Part 1, Parameters - Virtual Void ちなみに、意外と日本語の情報って少ない印象。playってあんま流行ってないのかな。
これを見るとこんな風に書いてあって、やっぱり、好きなFieldConstructorをフォームヘルパーに渡しています。implicitly[Lang]
というのは必要で、テンプレート側に暗黙的に渡ってくるLang
型の変数を入れるみたいな意味です。
@inputDate(field = ...)(fieldConstructor = dateConstructor, implicitly[Lang])
3. 実際に別々のFieldConstructorを指定してみる
ってことで、例えばdefaultFieldConstructor
divとか囲わなくていいっす。質素なやつでいいんです。みたいな場合には、こんなFieldConstructorを書いて、myTextInput.scala.html
として保存します。
@(elements: helper.FieldElements) <label for="@elements.id">@elements.label</label> @elements.input
あとは、呼び出し側で@inputTextのパラメータとしてそのFiledConstructorを渡してあげれば、
@inputText(computerForm("name"), '_label -> "Computer name")(myTextInput, implicitly[Lang])
こんなhtmlがでてきます。やった!
<label for="name">Computer name</label> <input type="text" id="name" name="name" value="">
別々のFieldConstructorでformのヘルパーで出力させることができました!
Formオブジェクトを渡すだけでうまいことやってくれるヘルパーなんですから、なんとしてもFieldConstructorでやりたかった訳で、これでhtmlを直書きするなんて残念なことはしなくていいわけですね。
まとめ
Scalaはimplicit
みたいな単語ががよく出てきますが、これは日本語にすると暗黙的
という意味です。
日本語にすれば至極簡単ですよね。その言葉通り、何もしなければ暗黙的に何かが適用されてるんだなと理解すればいいってことです。
FieldConstructorが別々のものが適用できることが分かったので、これでフォームヘルパーで怖いものはなくなりましたね!
第10回は、ログイン認証にしました。Play Framework 2.3 For Java ことはじめ #10 ログイン認証編