技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

可変長のフォームに対応する

まぁ特に難しい話はなし。

リピートしたい類のモデルを用意する。

package models

case class Game(name: String, vendor: String)

object Game {

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

  def form = Form(maps)

  def multiForm = Form(single(
    "games" -> seq(maps)
  ))

  def maps = mapping(
    "name" -> nonEmptyText,
    "vendor" -> nonEmptyText
  )(apply)(unapply)
}

で、これをView/Conttrollerにバインドして

package controllers

import play.api.mvc._
import models.Game

object Application extends Controller {

  def index = Action {
    val forms = Game.multiForm
    Ok(views.html.index(forms))
  }

  def result = Action { implicit request =>
    val forms = Game.multiForm.bindFromRequest()
    forms.value.foreach(games => {
      games.foreach(s => println(s"Game sending $s"))
    })
    Ok(views.html.index(forms))
  }
}

画面はこっち

@(form: Form[Seq[Game]])

@main("Welcome to Play") {
    @helper.form(routes.Application.result){
        @helper.repeat(form("games"), min = 2){ field =>
            @helper.inputText(field("name"))
            @helper.inputText(field("vendor"))
        }
        <input type="submit" value="送信!"/>
    }
}

routes を書いたら実行開始

# Home page
GET     /                           controllers.Application.index
POST    /                           controllers.Application.result

生成されたフォームは下記のような感じ

<form action="/" method="POST">
<dl class=" " id="games_0__name_field">
    <dt><label for="games_0__name">games[0].name</label></dt>
    <dd>
    <input type="text" id="games_0__name" name="games[0].name" value="ICO">
    </dd>
</dl>
<dl class=" " id="games_0__vendor_field">
    <dt><label for="games_0__vendor">games[0].vendor</label></dt>
    <dd>
    <input type="text" id="games_0__vendor" name="games[0].vendor" value="Sony">
    </dd>
</dl>
<dl class=" " id="games_1__name_field">
    <dt><label for="games_1__name">games[1].name</label></dt>
    <dd>
    <input type="text" id="games_1__name" name="games[1].name" value="BioHazard">
    </dd>
</dl>
<dl class=" " id="games_1__vendor_field">
    <dt><label for="games_1__vendor">games[1].vendor</label></dt>
    <dd>
    <input type="text" id="games_1__vendor" name="games[1].vendor" value="Capcom">
    </dd>
</dl>
        <input type="submit" value="送信!">
</form>

id の命名規則が「(parent forms field name)(index)__(child forms field name)field」らしい。

name 属性が「(parent form field name)[(index)].(child form field name)」ということか。

ということで、手動でフォームを追加しちゃおう。

@(form: Form[Seq[Game]])

@main("Welcome to Play") {
    @helper.form(routes.Application.result){
        @helper.repeat(form("games"), min = 2){ field =>
            @helper.inputText(field("name"))
            @helper.inputText(field("vendor"))
        }

        <dl class=" " id="games_2__name_field">
            <dt><label for="games_2__name">games[2].name</label></dt>
            <dd>
                <input type="text" id="games_2__name" name="games[2].name" value="BioHazard">
            </dd>
        </dl>
        <dl class=" " id="games_2__vendor_field">
            <dt><label for="games_2__vendor">games[2].vendor</label></dt>
            <dd>
                <input type="text" id="games_2__vendor" name="games[2].vendor" value="Capcom">
            </dd>
        </dl>
        
        <input type="submit" value="送信!"/>
    }
}

で、コンソールを眺めると

Game sending Game(ICO,Sony)
Game sending Game(BioHazard,Capcom)
Game sending Game(MetalGear,KONAMI)

良さげ。

動的にフォームを追加するなら JavaScript かねぇ?