2012-08-10 57 views
2

由於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」等?播放格式適用的方法是:

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") 
... 
+0

我不完全確定我得到你的問題Re:泛型。難道你不能把它作爲Form [CartOrder]參數化呢? 另外,關於用戶問題。你可以做的一件事是將用戶映射與用戶表單分開。然後,您可以在會議表單中重新使用該映射,然後在用戶表單中使用userMapping.withPrefix(「user」),以便訪問鍵保持一致。 – thatsmydoing 2012-08-10 16:28:35

+0

對,我一直在努力做到這一點;然而,它不像指定Form [CartOrder]那樣簡單,出現錯誤:「類型play.api.data.Form在類型T中不變」。所以編譯器接受會議從CartOrder派生,但是Play定義了Form [T]而不是Form [+ T] – virtualeyes 2012-08-10 16:50:11

+0

哦,好的。我不知道它是不變的。我可以向你建議的另一件事是,要有一個CartOrder類型的Form/Mapping,然後對所有可選字段使用可選映射,然後在主映射函數中使用可選映射,返回適當的具體類型。雖然 – thatsmydoing 2012-08-10 17:02:41

回答

1

確定,拖拖拉拉-O如下正是如此(幫腔,如果你有更好的方法):

play.api.data.Form [T]是不變的,所以沒有骰子傳入超類型有限公司參考(即CartOrder)添加到用戶表單。換句話說,這就炸了:

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

基本上你必須傳遞一個本身是Form可映射的實例。

要解決的樣板層房子的樂趣,我已經實現了下面的技巧:

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

編輯
變得更好,在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]) 

樣板的一個很好的協議被消除與一般的嵌套形式,因此整體,進展。將不會很依賴中間形式的映射器,但這是一樣好,我現在可以拿出...

2

如果類型安全不是一個問題(Form s不是所有類型的安全開始),您可以使用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) 

的implicits將處理來自窗體轉換爲FormView控件

一個完整的例子,可以發現:https://github.com/thatsmydoing/formtest

+0

+1。好奇的是,Play的表單實現是不是安全的?窗體[_]顯然會將類型安全性從窗口中移出,因此不會採用該路線。 – virtualeyes 2012-08-11 20:44:36

+0

無論你給form.apply()什麼鍵,它都會給你一個'Field',即使這個鍵不存在。這對模板很有用,因爲你可以使用Form [_],它仍然可以工作。另外,如果你有'Form'返回相同類型,但有不同的鍵,它們不完全是「相同」類型。 – thatsmydoing 2012-08-12 02:43:31

0

猜你想

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

,而不是建議

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