§フォームの送信
§フォームの定義
play.api.data
パッケージには HTTP フォームデータの送信とバリデーションを行うヘルパがいくつか含まれています。フォーム送信を処理する最も簡単な方法は、play.api.data.Form
を定義することです。
このフォームは Map[String, String]
型のデータから (String, String)
の値を生成することができます。
スコープ内にリクエストが存在する場合は、リクエストの内容から直接バインドすることができます。
Unable to find label loginForm-generate-request in source file code/ScalaForms.scala§複雑なオブジェクトの構築
フォームはある値を構築したり、分解する関数を引数に取ることができます。そのため、例えば次のように case class をラップするフォームを定義することができます。
val anyData = Map("name" -> "bob", "age" -> "18")
val user: UserData = userForm.bind(anyData).get
Note:
tuple
とmapping
の違いは、tuple
については構築および分解に使われる関数を指定する必要がないということです (タプルを構築、または分解する方法は明らかですよね) 。
mapping
メソッドには好きな関数を渡すことができます。ケースクラスを構築または分解する場合、デフォルトのapply
とunapply
関数がまさしくケースクラスの構築・分解を行う関数なので、それらを渡しておけば問題ありません。
Form
のシグネチャがケースクラスと一致しないこともあると思います。例えば、利用規約の同意を尋ねるためのチェックボックスを追加したフォームで考えてみましょう。このチェックボックスの値は User
に追加したくありません。なぜかというと、このダミーのフィールドはフォームをバリデーションするために利用しますが、バリデーションが終わった後は用済みだからです。
構築・分解用の関数を自前で定義して利用すると、このようなフォームも簡単に実現できます。
val userFormVerify = Form(
mapping(
"name" -> text,
"age" -> number,
"accept" -> checked("Please accept the terms and conditions")
)((name, age, _) => UserData(name, age))
((user: UserData) => Some(user.name, user.age, false))
)
Note: 分解用の関数は
User
の値をフォームに設定するときに利用されます。これは、例えばユーザ情報をデータベースから読み込んで、更新のために予めフォームに埋め込んでおくような場合に便利です。
§制約の定義
各々のマッピングについて、フォームのバインド時に実行されるバリデーションを追加することができます。
val userFormConstraints = Form(
mapping(
"name" -> text.verifying(nonEmpty),
"age" -> number.verifying(min(0), max(100))
)(UserData.apply)(UserData.unapply)
)
Note: 上記のコードは次のように書くこともできます。
val userFormConstraints2 = Form( mapping( "name" -> nonEmptyText, "age" -> number(min = 0, max = 100) )(UserData.apply)(UserData.unapply) )
このコードは、先程の例と同様に、バリデーションが追加された全く同じマッピングを表しています。
フィールドにアドホックなバリデーションを定義することもできます。
def validate(name: String, age: Int) = {
name match {
case "bob" if age >= 18 =>
Some(UserData(name, age))
case "admin" =>
Some(UserData(name, age))
case _ =>
None
}
}
val userFormConstraintsAdHoc = Form(
mapping(
"name" -> text,
"age" -> number
)(UserData.apply)(UserData.unapply) verifying("Failed form constraints!", fields => fields match {
case userData => validate(userData.name, userData.age).isDefined
})
)
§バインドエラーの処理
バリデーションを定義するということは、一方でバインドエラーを処理しなければならないということです。そのためには、fold
操作を利用することができます。
§フォームに初期値を設定する
よくあるケースとして、編集などのためにフォームに予め値を設定したい場合は、以下のようにします。
val filledForm = userForm.fill(UserData("Bob", 18))
§ネストした値
フォームはネストした値を扱うこともできます。
val userFormNested: Form[UserAddressData] = Form(
mapping(
"name" -> text,
"address" -> mapping(
"street" -> text,
"city" -> text
)(AddressData.apply)(AddressData.unapply)
)(UserAddressData.apply)(UserAddressData.unapply)
)
このような方法でネストしたデータを扱う場合、ブラウザから送信されたフォーム値の名前は address.street
や address.city
のような形式になっている必要があります。
§値の繰り返し
フォームは値の繰り返しを扱うこともできます。
val userFormRepeated = Form(
mapping(
"name" -> text,
"emails" -> list(email)
)(UserListData.apply)(UserListData.unapply)
)
このようなデータの繰り返しを処理する場合には、ブラウザから送信されるフォーム値の名前は emails[0]
, emails[1]
, examils[2]
のような形式になっている必要があります。
§省略可能な値
フォームは省略可能な値を扱うこともできます。
val userFormOptional = Form(
mapping(
"name" -> text,
"email" -> optional(email)
)(UserOptionalData.apply)(UserOptionalData.unapply)
)
Note: リクエストパラメータに
None
がセットされます。
§無視された値
フィールド用の静的な値を持つフォームが必要な場合は:
val userFormStatic = Form(
mapping(
"id" -> ignored(23L),
"name" -> text,
"email" -> optional(email)
)(UserStaticData.apply)(UserStaticData.unapply)
)
これまで説明した省略可能な値、値のネストや繰り返しに関するマッピングを組み合わせて、より複雑なフォームを定義することができます。
次ページ: フォームテンプレートヘルパーの利用
このドキュメントの翻訳は Play チームによってメンテナンスされているものではありません。 間違いを見つけた場合、このページのソースコードを ここ で確認することができます。 ドキュメントガイドライン を読んで、お気軽にプルリクエストを送ってください。