【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.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的时候也用了比较长的时间 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值