2

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.firstName」、「user.lastName」などにアクセスするにはどうすればよいですか?Playフォームの適用方法は次のとおりです。

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")
...
4

3 に答える 3

2

型の安全性が問題にならない場合(そもそも型の安全性がFormすべてではない場合)、Form[_]ネストされたテンプレートのパラメーター型として使用できます。


型の安全性が必要な場合、1つのオプションは、共変であるFormのラッパークラスを作成し、Formの代わりにそれを使用することです。1つの実装は次のとおりです。

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にあります。

于 2012-08-11T18:19:37.280 に答える
1

わかりました、dilly-oはこのように続きます(あなたがより良い方法を持っているならチャイムで):

play.api.data.Form [T]は不変であるため、スーパータイプの会議(つまりCartOrder)をユーザーフォームに渡すサイコロはありません。言い換えれば、これは爆発します:

// user.scala.html
@(_form: Form[CartOrder])

基本的に、それ自体がフォームマップ可能であるインスタンスを渡す必要があります。

テンプレートレイヤーのファンハウスを回避するために、次のハックを実装しました。

case class CartModel(user: User, payment: Payment)

EDIT
が改善され、bind以下のCartFormマッパーにヘルパーが追加されました。これにより、ビューの構文がクリーンになります。

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
}

次に、会議の親フォームで、次のようなユーザーフォームフィールドをプルします。

@user.nested( CartForm.bind(_form) )

そして、ユーザーフォームは以下を受け取ります。

@(_form: Form[CartModel])

一般にネストされたフォームを使用すると、ボイラープレートのかなりの部分が排除されるため、全体として進歩します。中間のフォームマッパーに頼らないのは素晴らしいことですが、これは私が今のところ思いつくことができる限り良いです...

于 2012-08-10T17:23:00.010 に答える
0

あなたが欲しいと思います

@(_form: Form[_ <: CartOrder])

提案する代わりに

@[T <: CartOrder](_form: Form[T])
于 2014-01-03T00:11:35.840 に答える