本文来自:http://fair-jm.iteye.com/ ?转截请注明出处
?
许久不更新...单纯是因为懒罢了...
?
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.helperobject 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的时候也用了比较长的时间?
?