In Play 2.4.0 如何在嵌套表单的多字段约束中命名字段?

Posted

技术标签:

【中文标题】In Play 2.4.0 如何在嵌套表单的多字段约束中命名字段?【英文标题】:In Play 2.4.0 How to NAME a Field in a multi-field Constraint on a nested Form? 【发布时间】:2015-06-08 13:40:55 【问题描述】:

这是对复杂表单问题的后续跟进:How to get child record (case class) ID in Play Framework (2.4.0) using Forms

lazy val aForm = Form(
mapping(
  "ID" -> ignored(id),
  "firstName" -> nonEmptyText,
  "lastName" -> nonEmptyText,
  "listOfEmails" -> mapping(
     "ID" -> ignored(emailID),
     "email" -> email,
     "userID" -> ignored(id),
     "emailTypeID" -> longNumber)
     (UserEmail.apply)(UserEmail.unapply) verifying someConstraint,
  "statusID" -> ignored(0l),
  "roleID" -> default(longNumber, roleID),
  "timezoneID" -> default(longNumber, timezoneID) 
  (User.apply) 
  (User.unapply)
  )

现在,由于约束在 apply 方法之后,我们拥有转换后的 case class 并访问它的所有字段,因此我们可以编写使用来自任何和所有字段的数据的验证。不确定我是否真的对此感到满意 - 为什么在测试之前转换数据 - 为什么我们不能简单地使用手头的数据进行验证过程?如果case class 本身由于错误的数据或内部验证过程而在创建过程中出现任何异常,也会出现问题,但这将是一个足够小的极端情况来构建解决方法 - 仅用于验证的自定义 case class .

   def someConstraint: Constraint[UserEmail] =  Constraint("constraints.unique")(
     userEmail =>
          match doStuff(userEmail.ID, userEmail.email, userEmail. emailTypeID) 
          case BAD => Invalid(Seq(ValidationError("error.unique.email.required")))
          case GOOD => Valid
          
       
    |)

很抱歉伪代码,但希望您对验证过程中发生的事情有基本的了解。我们获取几个字段并处理它们包含的数据以进行验证判断调用。

事情就是这样。

html 输出中,嵌套类字段必须引用为:listOfEmails[x].IDlistOfEmails[x].email 等,以便 Play 框架在 POST 上正确捕获它们 -- https://www.playframework.com/documentation/2.4.0/ScalaForms

但是,约束过程返回 listOfEmails[x] 作为错误键的字段名称,因此它不会出现在 html 模板上,因为该键不匹配任何内容。

那么如何重命名错误字段键(假设这是正确的答案)或者更好的问题是如何使这一过程以端到端的方式进行?

是的,我们可以使用全局错误,但是在大型复杂的表单上,我们希望错误接近解决方案,以便用户的眼球能够快速识别并继续前进。

【问题讨论】:

【参考方案1】:
object TestSeq extends App 
//case class Email(email: String)
  case class Email(email: String,f1:String,f2:String,f3:String,f4:String,f5:String,f6:String,f7:String,f8:String,f9:String)
  case class User(name: String, emails: Seq[Email], addr: String)

  import play.api.data.Forms._
  import play.api.data._
  import play.api.data.validation._

  val someConstraint: Constraint[Email] =
    Constraint("constraints.unique")  email =>
      if(email.email.contains('@')) Valid
      else Invalid(ValidationError("error.unique.email.required"))
    

  val form = Form(
    mapping(
      "name" -> text,
      "emails" -> seq(mapping(
        "email" -> nonEmptyText,
        "f1" -> text,
        "f2" -> text,
        "f3" -> text,
        "f4" -> text,
        "f5" -> text,
        "f6" -> text,
        "f7" -> text,
        "f8" -> text,
        "f9" -> text
      )(Email.apply)(Email.unapply) verifying someConstraint),
      "addr" -> text
    )(User.apply)(User.unapply)
  )

  val data = Map[String, String](
    "name" -> "xx",
    "emails[0].email" -> "a0@xx.com",
    "emails[0].f1" -> "0f1",
    "emails[0].f2" -> "0f2",
    "emails[0].f3" -> "0f3",
    "emails[0].f4" -> "0f4",
    "emails[0].f5" -> "0f5",
    "emails[0].f6" -> "0f6",
    "emails[0].f7" -> "0f7",
    "emails[0].f8" -> "0f8",
//    "emails[0].f9" -> "0f9",
    "emails[1].email" -> "",
    "emails[1].f1" -> "1f1",
    "emails[1].f2" -> "1f2",
    "emails[1].f3" -> "1f3",
    "emails[1].f4" -> "1f4",
    "emails[1].f5" -> "1f5",
    "emails[1].f6" -> "1f6",
    "emails[1].f7" -> "1f7",
    "emails[1].f8" -> "1f8",
    "emails[1].f9" -> "1f9",
    "emails[2].email" -> "a2xx.com",
    "emails[2].f1" -> "2f1",
    "emails[2].f2" -> "2f2",
    "emails[2].f3" -> "2f3",
    "emails[2].f4" -> "2f4",
    "emails[2].f5" -> "2f5",
    "emails[2].f6" -> "2f6",
    "emails[2].f7" -> "2f7",
    "emails[2].f8" -> "2f8",
    "emails[2].f9" -> "2f9",
    "addr" -> "dalian"
  )

  val user = form.bind(data).fold(
    ef => ef.errors.foreach(println _) ,
    contact =>println(contact)
  )

  /* ===== output =====
  FormError(emails[0].f9,List(error.required),List())
  FormError(emails[1].email,List(error.required),WrappedArray())
  FormError(emails[2],List(error.unique.email.required),WrappedArray())
  user: Unit = ()
  */

【讨论】:

请阅读***.com/help/how-to-answer + 很高兴为这段代码提供一些上下文和解释。 本质上这已将seq 添加到子案例类的映射中。让我想知道我是否没有把它放在示例代码中。我想我记得在尝试将多字段案例类与seq 混合时遇到了可怕的问题(这里的示例是单字段案例类) """本质上这已经将 seq 添加到子案例类的映射中。""" 是的。 @Techmag 我将代码更改为多字段。 对不起,我还没有回到这个截止日期 :( 但是它在我需要验证的事情列表中。

以上是关于In Play 2.4.0 如何在嵌套表单的多字段约束中命名字段?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Ruby on Rails 中管理嵌套字段的多对多关系

为啥我的嵌套 POJO 从 Play 中的表单返回 null! 1.x

如何在不创建其他字段的情况下生成一个列表的嵌套 formik 表单或表单字段?

如何在 play 2.4 中的 scala 模板中设置类型列表字段的值?

如何动态访问嵌套错误/触及formik字段

如何使用 Postman 表单数据在 Django REST Framework 中发布嵌套数组?