前書き

Playのテンプレートでhelperを使ってHTMLフォームを出力してみる。

Play2.0.4利用。

@CretedDate 2012/11/03
@Versions Scala2.9.2 Play2.0.4

サンプルアプリの用意

とりあえずプロジェクト作成。

$ play new form_helper_example
$ cd form_helper_example

@helper.formでフォームを書いてみる

app/views/index.scala.htmlを編集して、下記のような感じで書いてみる。

@(form: Form[Application.User])

@main("たいとる") {
    @helper.form(action = routes.Application.index) {
        @helper.inputText(form("name"), 'size -> 10)
        @helper.inputText(form("age"), 'size -> 3)
        @helper.inputRadioGroup(
            form("sex"),
            options = Seq("male" -> "男", "female" -> "女"),
            '_label -> "sex")
        <input type="submit" value="さぶみっしょん">
    }
}

@helper.formでフォームを宣言して、@helper.inputTextとか@helper.inputRadioGroupなどで入力フォームを生成する。

フォームの値を受け取る項で使ったFormクラスで値を渡せば、入力された値でフォームが埋められる動きを手軽に書ける。

次にController側でFormクラスを用意してViewに渡す処理を書く。

  import play.api.data.Form
  import play.api.data.Forms._

  case class User(name: String, age: Int, sex: String)
  val form = Form(mapping("name" -> text, "age" -> number, "sex" -> text)(User.apply)(User.unapply))

  def index = Action { implicit request =>
    val user = form.bindFromRequest
    Ok(views.html.index(user))
  }

これで概ね動くようになった。エラーメッセージとかも自動出力される、それっぽいフォームが出力される。

出力されるHTMLはこんな感じ。

<form action="/" method="GET" >
    <dl class=" " id="name_field">
        <dt><label for="name">name</label></dt>
        <dd><input type="text" id="name" name="name" value="aa" size="10"></dd>
    </dl>
    <dl class=" " id="age_field">
        <dt><label for="age">age</label></dt>
        <dd><input type="text" id="age" name="age" value="13" size="3"></dd>
        <dd class="info">Numeric</dd>
    </dl>
    <dl class=" error" id="gender_field">
        <dt><label for="gender">gender</label></dt>
        <dd>
            <span class="buttonset" id="gender">
                <input type="radio" id="gender_male" name="gender" value="male"  >
                <label for="gender_male">男</label>
                <input type="radio" id="gender_female" name="gender" value="female"  >
                <label for="gender_female">女</label>
            </span>
        </dd>
        <dd class="error">This field is required</dd>
    </dl>
    <input type="submit" value="さぶみっしょん">
</form>

dl/ddで書いている。呼び出す時にclass指定してちゃんとスタイル書けばそこそこに見栄えは調整できる。

ここで使ったテキストボックスとラジオボタン以外にも、セレクトボックス、チェックボックス、テキストエリア、日付入力フィールド等、一通り用意されている。

HTMLで似たようなことをする

ヘルパーは簡易なアプリを書く時は便利だけど、デザインを凝り出すと逆に面倒だったり、覚えるのに多少時間がかかる等のデメリットもある。

HTMLをベタ書きしたい場合は、こんな感じでも良いだろうか。

@(form: Form[Application.User])

@main("たいとる") {
  <form action="/">
    <input type="text" name="name"  value="@form.data.getOrElse("name", "")"><br>
    <input type="text" name="age"  value="@form.data.getOrElse("age", "")"><br>
    <input type="radio" name="gender"  value="male" @(if(form.data.getOrElse("gender", "") == "male") "checked")>男
    <input type="radio" name="gender"  value="female" @(if(form.data.getOrElse("gender", "") == "female") "checked")>女
    <input type="submit" value="さぶみと">
  </form>
}

自前で部品書いておいてincludeしても良いかもしれない。

例えばapp/viewsにradio.scala.htmlという名前のファイルを作って、下記のように記述する。

@(name: String, value: String, formData: Map[String, String], options: Map[String, String] = Map[String, String]())

<input type="radio" name="@name"  value="@value" @(if(formData.getOrElse(name, "") == value) "checked") @for( (key, value) <- options ) { @key="@value" }>

あとはこの子を呼び出せば、自前のラジオボタン用部品の出来上がり、みたいな。

  <form action="/">
    <input type="text" name="name"  value="@form.data.getOrElse("name", "")"><br>
    <input type="text" name="age"  value="@form.data.getOrElse("age", "")"><br>
    @radio("gender", "male", form.data, Map("class" -> "hoge")) 男
    @radio("gender", "female", form.data) 女
    <input type="submit" value="さぶみと">
  </form>

Bootstrapを利用する

Twitter Bootstrapを利用することもできる。helper.twitterBootstrap._をimportすれば良いようだ。

@(form: Form[Application.User])

@import helper.twitterBootstrap._

@main("たいとる") {
    @helper.form(action = routes.Application.index) {
        @helper.inputText(form("name"), 'size -> 10)

以下略

こうするとTwitter Bootstrap用のHTMLが出力される。

<form action="/" method="GET" >
    <div class="clearfix  " id="name_field">
        <label for="name">name</label>
        <div class="input">
            <input type="text" id="name" name="name" value="aa" size="10">
            <span class="help-inline"></span>
            <span class="help-block"></span> 
        </div>
    </div>
    <div class="clearfix  " id="age_field">
        <label for="age">age</label>
        <div class="input">
            <input type="text" id="age" name="age" value="13" size="3">
            <span class="help-inline"></span>
            <span class="help-block">Numeric</span> 
        </div>
    </div>
    <div class="clearfix  error" id="gender_field">
        <label for="gender">gender</label>
        <div class="input">
            <span class="buttonset" id="gender">
                <input type="radio" id="gender_male" name="gender" value="male"  >
                <label for="gender_male">男</label>
                <input type="radio" id="gender_female" name="gender" value="female"  >
                <label for="gender_female">女</label>
            </span>
            <span class="help-inline">This field is required</span>
            <span class="help-block"></span> 
        </div>
    </div>
    <input type="submit" value="さぶみっしょん">
</form>

あとはTwitter BootStrapを落としてきて、CSSをpublic/stylesheets配下に置いて呼び出すと、表示がちょっとそれっぽくなる。