【发布时间】:2012-08-07 20:52:12
【问题描述】:
由于 Play 模板层缺乏泛型支持,遇到了 catch 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.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[CartOrder] 吗?顺便说一句,关于用户问题。您可以做的一件事是将您的用户映射与用户表单分开。然后,您可以在会议表单中重用映射,然后在用户表单中使用 userMapping.withPrefix("user") 以使访问密钥保持一致。
-
对,我实际上一直在努力这样做;但是它不像指定 Form[CartOrder] 那样简单,会报错:“class play.api.data.Form 在类型 T 中是不变的”。所以编译器接受 Conference 派生自 CartOrder,但 Play 定义了 Form[T] 而不是 Form[+T]
-
哦,好的。我不知道它是不变的。我可以向您建议的另一件事是,拥有一个 CartOrder 类型的表单/映射,然后对所有可选字段使用可选映射,然后在主映射函数中返回适当的具体类型。虽然不是特别优雅
-
我稍后会发布我的解决方案,基本上你必须传入超级类型的会议、订阅等购物车模型,我称之为 CartModel 或 CartOrder,等等。这个超类型本身必须包含用户和支付的表单映射。这还不错,但没有直接 T <: supertype>
标签: forms scala generics playframework