play.api.data.Form
Controller中使用:
创建实体:
- case class User(username: String,realname: Option[String],email: String)
创建Form对象
- val userForm = Form(
- mapping( //制定验证规则
- "username" -> nonEmptyText(8),
- "realname" -> optional(text),
- "email" -> email)(User.apply)(User.unapply))
- def createUser() = Action { implicit request =>
- userForm.bindFromRequest.fold(
- formWithErrors => BadRequest, //有错误的情况 得到的还是一个Form 可以通过getErrors获得错误
- user => Ok("User OK!")) //没有错误的情况 得到的是一个User对象
- }
fold的返回值是play.api.mvc.SimpleResult 是BadRequest和Ok的祖先
表格的Mapping 做binding(绑定)和unbinding的操作
把数据放入Form中叫做绑定
Forms是不可变的 binding之后返回一个新的拷贝
一个mapping是一个Mapping[T]对象
以上的代码中 Forms.nonEmptyText 创建了一个Mapping[String] email创建了一个Mapping[String] 更多的Forms.number创建了一个Mapping[Int]
更多的:
- ■boolean: Mapping[Boolean]
- ■ checked(msg: String): Mapping[Boolean]
- ■ date: Mapping[Date]
- ■ email: Mapping[String]
- ■ ignored[A](value: A): Mapping[A]
- ■ longNumber: Mapping[Long]
- ■ nonEmptyText: Mapping[String]
- ■ number: Mapping[Int]
- ■ sqlDate: Mapping[java.sql.Date]
- ■ text: Mapping[String]
form helper:
可以手动写name和mapping中的对应就好了要获取form内的值用data属性就可以了 data是Map对象
也可以借助helper:
- @(productForm: Form[Product])
- @main("Product Form") {
- @helper.form(action = routes.GeneratedForm.create) {
- @helper.inputText(productForm("name"))
- @helper.textarea(productForm("description"))
- @helper.inputText(productForm("ean"))
- @helper.inputText(productForm("pieces"))
- @helper.checkbox(productForm("active"))
- <div class="form-actions">
- <button type="submit">Create Product</button>
- </div>
- }
- }
会自动生成html 如果要自定义提示信息 查看一下trait Constraints Scaladoc
helper的input:
■ inputDate —Generates an input tag with type date.
■ inputPassword —Generates an input tag with type password .
■ inputFile —Generates an input tag with type file.
■ inputText —Generates an input tag with type text.
■ select —Generates a select tag.
■ inputRadioGroup —Generates a set of input tags with type radio .
■ checkbox —Generates an input tag with type checkbox .
■ textarea —Generates a textarea element.
■ input —Creates a custom input.
可以增加自定义的属性:
- @helper.inputText(productForm("name"), '_class -> "important",
- 'size -> 40)
'size和'_class是symbol类型的
更多的属性:
■ _label —Use to set a custom label
■ _id —Use to set the id attribute of the dl element
■ _class —Use to set the class attribute of the dl element
■ _help —Use to show custom help text
■ _showConstraints —Set to false to hide the constraints on this field
■ _error —Set to a Some[FormError] instance to show a custom error
■ _showErrors —Set to false to hide the errors on this field
自定义input:
- @helper.input(myForm("mydatetime")) { (id, name, value, args) =>
- <input type="datetime" name="@name"
- id="@id" value="@value" @toHtmlArgs(args)>
- }
第二个参数接受的是一个函数 参数是(String,String,Option[String],Map[Symbol,Any])
(toHtmlArgs来自play.api.templates.PlayMagic)
生成自定义的html:
在views.helper中(也可以是其他包啦 在views下面方便一点) 建立
myFileConstructor.scala.html
写入自定义html:
- @(elements: helper.FieldElements)
- <div id="@(elements.id)_field" class="clearfix @if(elements.hasErrors) {text-error}">
- <label for="name">@elements.label</label>
- <div class="input">
- @elements.input
- <span class="help-inline">@elements.errors.mkString(", ")</span>
- </div>
- </div>
第一个参数是helper.FieldElements 别忘了
然后再建立一个object:
- package views.html.helper
- object myHelper {
- implicit val fieldConstructor = new FieldConstructor {
- def apply(elements: FieldElements) =
- myFileConstructor(elements)
- }
- }
最后在需要的html中导入即可:
@import helper.myHelper._
原本就有一个是twitter的:
import helper.twitterBootstrap._*
自定义约束
为Mapping[T]增加约束可以使用verifying(constraints:Constraint[T *)方法( play.api.data.validation.Constraints)
使用如下:"name" -> text.verifying(Constraints.nonEmpty)
自定义约束用verifying实现也很简单 只要向里面传入一个T => Boolean方法即可
例如:
def eanExists(ean: Long) = Product.findByEan(ean).isEmpty
在mapping中:
"ean" -> longNumber.verifying(eanExists(_))
或者简写:
"ean" -> longNumber.verifying(Product.findByEan(_).isEmpty)
也可以带一个错误信息当作第一个参数:
"ean" -> longNumber.verifying("This product already exists.",
Product.findByEan(_).isEmpty)
验证多个域:
因为mapping所得的也是个Mapping[T]所以写起来很简单:
- val productForm = Form(mapping(
- "ean" -> longNumber.verifying("This product already exists!",Product.findByEan(_).isEmpty),
- "name" -> nonEmptyText,
- "description" -> text,
- "pieces" -> number,
- "active" -> boolean)(Product.apply)(Product.unapply).verifying(
- "Product can not be active if the description is empty",
- product =>
- !product.active || product.description.nonEmpty))
这样写有一个问题 错误信息将不会自动生成 因为顶层的Mapping在html中没有id
要自己手动生成 顶层的Mapping产生的错误在Form中是globalError:
- @productForm.globalError.map { error =>
- <span class="error">@error.message</span>
- }
option可选的:
- case class Person(name: String, age: Option[Int])
- val personMapping = mapping(
- "name" -> nonEmptyText,
- "age" -> optional(number) //用optional会返回Mapping[Option[T]]
- )(Person.apply)(Person.unapply)
list:
如果tags是个List可以写成:
"tags" -> list(text) 他将返回Mapping[List[T]]
如果是手动写的话可以写成:
<input type="text" name="tags[0]">
<input type="text" name="tags[1]">
<input type="text" name="tags[2]">
用helper的话 用repeat方法:
- @helper.repeat(form("tags"), min = 3) { tagField =>
- @helper.inputText(tagField, '_label -> "Tag")
- }
tuple and mapping methods take a maximum of 18 parameters.
嵌套:
- val appointmentMapping = tuple(
- "location" -> text,
- "start" -> tuple(
- "date" -> date,
- "time" -> text),
- "attendees" -> list(mapping(
- "name" -> text,
- "email" -> email)(Person.apply)(Person.unapply))
- )
返回的是:
Mapping[(String,(Date, String),List[Person])]
自定义Mapping
除了tuple(返回tuples)和mapping(返回对象) 自定义一个:
改变一个现有的(transform)
新建一个
转换类似于后处理:
如果你有一个Mapping[A] 也有一个A=>B 那可以用transform得到Mapping[B]
- val localDateMapping = text.transform(
- (dateString: String) =>
- LocalDate.parse(dateString),
- (localDate: LocalDate) =>
- localDate.toString)
不过这样的话 要注意parse可能抛出异常
新建一个formatter
play.api.data.format.Formatter
要实现Formatter[T]的方法:
- trait Formatter[T] {
- def bind(key: String, data: Map[String, String]):Either[Seq[FormError], T]
- def unbind(key: String, value: T): Map[String, String]
- val format: Option[(String, Seq[Any])] = None
- }
- implicit val localDateFormatter = new Formatter[LocalDate] {
- def bind(key: String, data: Map[String, String]) =
- data.get(key) map { value =>
- Try {
- Right(LocalDate.parse(value))
- } getOrElse Left(Seq(FormError(key, "error.date", Nil)))
- } getOrElse Left(Seq(FormError(key, "error.required", Nil)))
- def unbind(key: String, ld: LocalDate) = Map(key -> ld.toString)
- override val format = Some(("date.format", Nil))
- }
并向messages中添加:
date.format=Date (YYYY-MM-DD)
error.date=Date formatted as YYYY-MM-DD expected
再用Forms.of讲其转化为Mapping[T]:
val localDateMapping = Forms.of(localDateFormatter)得到Mapping[LocalDate] 因为of的参数是implict上面也写了localDateFormatter是implicit的
也可以写成:
val localDateMapping = Forms.of[LocalDate]
最后使用就可以获得Form了:
val localDateForm = Form(single(
"introductionDate" -> localDateMapping
))
single和tuple是一样的 只有一个参数的话可以用他
文件上传:
如果是单一的文件上传的话:
手工写form的格式:
- <form action="@routes.FileUpload.upload" method="post" enctype="multipart/form-data">
- <input type="file" name="image">
- <input type="submit">
- </form>
Action的处理:
- def upload() = Action(parse.multipartFormData) { request =>
- request.body.file("image").map { file =>
- file.ref.moveTo(new File("/tmp/image"))
- Ok("Retrieved file %s" format file.filename)
- }.getOrElse(BadRequest("File missing!"))
- }
和form一起的操作:
- def upload() = Action(parse.multipartFormData) { implicit request =>
- val form = Form(tuple(
- "description" -> text,
- //因为FilePart参数得不到 自己手工写到ignored里
- "image" -> ignored(request.body.file("image")).verifying("File missing", _.isDefined))
- )
- form.bindFromRequest.fold(
- formWithErrors => {
- Ok(views.html.fileupload.uploadform(formWithErrors))
- },value => Ok)
- }
于是这个Form就变成了:
Form[(String,Option[play.api.mvc.MultipartFormData.FilePart[play.api.libs.Files.TemporaryFile]])]
用helper的话:
- @(form:Form[_])
- @helper.form(action = routes.FileUpload.upload,'enctype -> "multipart/form-data") {
- @helper.inputText(form("description"))
- @helper.inputFile(form("image"))
- }
不过这样做的话就不能用upload里的form来传递给空白页了因为在实现里用到了request
- def showUploadForm() = Action {
- val dummyForm = Form(ignored("dummy"))
- Ok(views.html.fileupload.uploadform(dummyForm))
- }
可以用一个什么都不做的表格
form的内容是比较多的 以上的笔记可能有做得不是很容易理解的地方 需要自己多尝试一下 当初学form的时候也用了比较长的时间
本文来自:http://fair-jm.iteye.com/