首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不支持泛型的嵌套对象:变通方法

不支持泛型的嵌套对象:变通方法
EN

Stack Overflow用户
提问于 2012-08-10 22:17:46
回答 3查看 357关注 0票数 2

由于在Play模板层中缺乏泛型支持,命中了Catal22的情况。

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

代码语言:javascript
复制
case class Conference(
  user: User,
  payment: Payment
  ... custom fields here
)

因此,我没有重复每个购物车模型的所有用户和付款表单字段,而是像上面那样合并并实现了嵌套表单。

现在,问题出现在模板层,到目前为止还没有泛型支持。

父/容器窗体拉入嵌套子窗体,如下所示:

代码语言:javascript
复制
@(_form: Form[Conference])

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

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

代码语言:javascript
复制
@(_form: Form[User])

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

和用户模型:

代码语言:javascript
复制
case class User(firstName: String, lastName: String ...)

当用户模型中没有用户属性时如何访问"user.firstName“、"user.lastName”等?播放形式应用方法为:

代码语言:javascript
复制
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,这显然是行不通的。

我考虑过在用户模型中添加一个用户属性:

代码语言:javascript
复制
case class User(firstName: String, lastName: String ...) {
  val user: User
}

但不确定这是否可以工作和/或破坏case类伴生对象apply/unapply。

无论如何,鉴于缺乏泛型,有什么可行的解决方案来解决这个问题?

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

代码语言:javascript
复制
trait CartOrder {
  user: User,
  payment: Payment
}
case class Conference(...) extends CartOrder

然后向嵌套的用户表单传递一个包含用户属性的实例,这样就很好了

代码语言:javascript
复制
@[T <: CartOrder](_form: Form[T])
@inputText(_form("user.firstName"), '_label-> "First Name*", 'class-> "required")
...
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-08-11 01:23:00

好的,dilly-o跟在后面(如果你有更好的方法,请加入):

会议是不变的,所以没有骰子将超级类型的play.api.data.FormT (即CartOrder)传递给用户表单。换句话说,这就放大了:

代码语言:javascript
复制
// user.scala.html
@(_form: Form[CartOrder])

基本上,您必须传入一个本身是表单可映射的实例。

为了解决模板层的有趣之处,我实现了以下hack:

代码语言:javascript
复制
case class CartModel(user: User, payment: Payment)

编辑

变得更好了,在下面的CartForm映射器中添加了一个bind助手,这使得视图中的语法清晰

代码语言:javascript
复制
object CartForm {
  import play.api.data.Forms._  
  val form = 
    Form( mapping(
      'user -> UserForm.mapper,
      'payment -> PaymentForm.mapper )(CartModel.apply)(CartModel.unapply) )

  // hey, generics!
  def bind[T <: Form[_ <: CartContract]](t: T) =
    t.value map{x=> form.fill( CartModel(x.user, x.payment) )} getOrElse form
}

然后在Conference父表单中,像这样拉入用户表单字段:

代码语言:javascript
复制
@user.nested( CartForm.bind(_form) )

然后,用户表单接收到:

代码语言:javascript
复制
@(_form: Form[CartModel])

通常情况下,嵌套表单消除了大量的样板文件,因此总体上是有进步的。不依赖于中介表单映射器是很好的,但这是我目前所能想到的最好的方法……

票数 1
EN

Stack Overflow用户

发布于 2012-08-12 02:19:37

如果不关心类型安全(Form从一开始就不是所有类型安全类型),那么您可以只使用Form[_]作为嵌套模板的参数类型。

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

代码语言:javascript
复制
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
}

现在,您的模板不是

代码语言:javascript
复制
@(_form: Form[CartOrder])

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

代码语言:javascript
复制
@(_form: FormView[CartOrder])

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

代码语言:javascript
复制
@user.nested(_form)

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

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

票数 2
EN

Stack Overflow用户

发布于 2014-01-03 08:11:36

我猜你想

代码语言:javascript
复制
@(_form: Form[_ <: CartOrder])

而不是建议

代码语言:javascript
复制
@[T <: CartOrder](_form: Form[T])
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11903593

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档