当前位置: 代码迷 >> .NET Framework >> 【playframework2札记整理】4、form
  详细解决方案

【playframework2札记整理】4、form

热度:115   发布时间:2016-05-01 23:34:32.0
【playframework2笔记整理】4、form

本文来自: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属性就可以了 dataMap对象
也可以借助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的时候也用了比较长的时间?

?

  相关解决方案