forms - 不支持泛型的嵌套对象 : workarounds

标签 forms scala generics playframework

由于 Play 模板层缺乏泛型支持,遇到了第 22 条军规的情况。

我有几个购物车屏幕,它们都需要用户和付款+可选的自定义字段。

case class Conference(
  user: User,
  payment: Payment
  ... custom fields here
)

因此,我没有重复每个购物车模型的所有用户和付款表单字段,而是按上述方式进行了整合并实现了嵌套表单。

现在,问题出现在模板层,迄今为止还没有泛型支持。

父/容器表单拉入嵌套的子表单,如下所示:

@(_form: Form[Conference])

@user.nested( UserForm.form.fill(_form.get.user) )
@payment.nested( PaymentForm.form.fill(_form.get.payment) )

然后子用户表单如下所示:

@(_form: Form[User])

@inputText(_form("user.firstName"), '_label-> "First Name*", 'class-> "required")
@inputText(_form("user.lastName"), '_label-> "Last Name*", 'class-> "required")
...

和用户模型:

case class User(firstName: String, lastName: String ...)

当 User 模型中没有 user 属性时,如何访问“user.firstName”、“user.lastName”等? Play Form申请方法为:

def apply(key: String): Field = Field(
    this,
    key,
    constraints.get(key).getOrElse(Nil),
    formats.get(key),
    errors.collect { case e if e.key == key => e },
    data.get(key))

基本上,它会寻找属性data.user.firstName,这显然是行不通的。

我考虑过向用户模型添加用户属性:

case class User(firstName: String, lastName: String ...) {
  val user: User
}

但不确定这是否会起作用和/或对案例类伴生对象应用/取消应用造成严重破坏。

无论如何,鉴于缺乏泛型,什么是解决该问题的可行解决方案?

如果支持泛型,我们可以传入一个上限,一切都会很美好:

trait CartOrder {
  user: User,
  payment: Payment
}
case class Conference(...) extends CartOrder

然后向嵌套的用户表单传递一个包含用户属性的实例,我们就可以了

@[T <: CartOrder](_form: Form[T])
@inputText(_form("user.firstName"), '_label-> "First Name*", 'class-> "required")
...

最佳答案

如果类型安全不是问题(Form 并不是所有类型都安全),您可以使用 Form[_] 作为嵌套模板的参数类型。


如果您确实想要类型安全,一种选择是为协变的 Form 创建一个包装类,并使用它来代替 Form。一种实现是:

package views.html

import play.api.data._
import play.api.libs.json.JsValue

object FormView {
    implicit def formToFormView[A, T >: A](form: Form[A]): FormView[T] = new FormView[T] {
        type F = A
        def realForm = form
    }
}

trait FormView[+T] {
    type F <: T

    def realForm: Form[F]

    def apply(key: String): Field = realForm(key)

    def constraints : Map[String, Seq[(String, Seq[Any])]] = realForm.constraints

    def data: Map[String, String] = realForm.data

    def error(key: String): Option[FormError] = realForm.error(key)

    def errors(key: String): Seq[FormError] = realForm.errors(key)

    def errors: Seq[FormError] = realForm.errors

    def errorsAsJson: JsValue = realForm.errorsAsJson

    def get: T = realForm.get

    def formats: Map[String, (String, Seq[Any])] = realForm.formats

    def globalError: Option[FormError] = realForm.globalError

    def globalErrors: Seq[FormError] = realForm.globalErrors

    def hasErrors: Boolean = realForm.hasErrors

    def hasGlobalErrors: Boolean = realForm.hasGlobalErrors

    override def hashCode: Int = realForm.hashCode

    def mapping: Mapping[F] = realForm.mapping

    def value: Option[T] = realForm.value
}

现在你的模板不再是

@(_form: Form[CartOrder])

由于不变性,这不起作用,您可以使用

@(_form: FormView[CartOrder])

您可以简单地传入任何 Form[T],其中 TCartOrder 的子类型,例如

@user.nested(_form)

隐式函数将处理从 Form 到 FormView 的转换

完整的示例可以在以下位置找到:https://github.com/thatsmydoing/formtest

关于forms - 不支持泛型的嵌套对象 : workarounds,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11903593/

相关文章:

swift - 如何在 Swift 上为堆栈声明 "if optional"

jquery - 如何识别 child 输入类型

scala - 在 Scala 中将矩阵 9x9 元素分组为 3 行 3 列

javascript - 使用 datetimepicker 防止 f.text_field 中的自定义输入(Rails 3.2,Bootstrap)

scala - Scala 中丰富的枚举

scala - 如何使用带有 SSL 证书的 Akka 发送 HTTP 请求

c# - 枚举的通用类 - 转换问题

generics - Kotlin 编译器混合具体化类型参数

php - <form> 标签在用 php 创建时不显示

forms - 电子邮件地址中允许使用哪些字符?