2012-08-29 39 views
6

Değişen sayıda girdi alanıyla formları dinamik olarak nasıl oluştururum?Dinamik form oluşturma ile yesod

listEditForm :: [String] -> Html -> MForm App App (FormResult Text, Widget) 
listEditForm xs = renderDivs $ mconcat [ areq textField (String.fromString x) Nothing | x <- xs] 

ancak bu Text ve beklendiği gibi değil [Text], tesadüf sahip Text olduğu sonuç tipi Monoid bir örneğini, örn var

Yönetilen yakın olan Int ile başarısız olur.

Çeşitli formları bir araya getiren bir çalışma alternatif girişimim var, ancak bir şekilde bu oyuncak örneği için çalışıyor, ancak gerçek girişim tuhaf bir şekilde başarısız oluyor. Her neyse, bu doğru bir yaklaşım olduğunu düşünmüyorum:

data MapPair = MapPair { mpKey :: T.Text, mpValue :: Maybe T.Text } 

editForm mmp = renderTable $ MapPair 
    <$> areq textField "Key" (mpKey <$> mmp) 
    <*> aopt textField "Value" (mpValue <$> mmp) 

pair2mp (v,k) = MapPair { mpKey = v, mpValue = Just k } 

getEditR = do 
    sess <- getSession 
    let sesslist = Map.toList $ Map.map (decodeUtf8With lenientDecode) sess 
    forms <- forM sesslist (\a -> generateFormPost $ editForm $ Just $ pair2mp a) 

    defaultLayout [whamlet| 
    <h1>Edit Value Pairs 
    $forall (widget,enctype) <- forms 
     <form method=post [email protected]{EditR} enctype=#{enctype}> 
     ^{widget} 
     <input type=submit> 
    |] 

    postEditR = do 
    sess <- getSession 
    let sesslist = Map.toList $ Map.map (decodeUtf8With lenientDecode) sess 
    forM_ sesslist (\a -> do 
     ((res,_),_) <- runFormPost $ editForm $ Just $ pair2mp a 
     case res of 
      (FormSuccess (MapPair {mpKey=mk, mpValue=(Just mv)})) -> setSession mk mv 
      _ -> return() 
    ) 
    defaultLayout [whamlet|ok|] 

cevap

6

Duh, aslında kolay monadic formları kullanarak (aşağıda kodu bakınız). Başlıca baş ağrım, yanıtı alan işleyicinin, ilgili soruyu da çözebildiğinden emin olmak için fazladan metin alanlarıdır. Belki bu metin alanlarını gizleyebilir, onları düzenlenemez yapabilir ya da bu konuda başka bir yol bulabilirim (ama henüz Html hakkında fazla bir şey bilmiyorum).

listEditMForm :: [(String,Int)] -> Html -> MForm App App (FormResult [(FormResult Int, FormResult Text)], Widget) 
listEditMForm xs extra = do 
    ifields <- forM xs (\(s,i) -> mreq intField (String.fromString s) (Just i)) 
    tfields <- forM xs (\(s,i) -> mreq textField (String.fromString s) (Just $ pack s)) 
    let (iresults,iviews) = unzip ifields 
    let (tresults,tviews) = unzip tfields 
    let results = zip iresults tresults 
    let views = zip iviews tviews 
    let widget = [whamlet| 
     #{extra} 
     <h1>Multi Field Form 
     $forall (iv,tv) <- views 
      Field # 
      #{fvLabel iv}: # 
      ^{fvInput tv} # 
      ^{fvInput iv} 
      <div> 
     |] 
    return ((FormSuccess results), widget) 

Orada her zaman en dış FormSuccess yapıcı daima sonucu sarma gibi, hakkında hiçbir ipucu var bazı çirkin şeyler hala da vardır, ama gerçekten her bir kullanım örneği bağlı olduğunu tahmin (örneğin tek FormFailure veya FormMissing muhtemelen tüm formu başarısız/eksik de yapmalıdır, ancak belki de bazı durumlarda bu istenmez.)

Tüm sıkıştırma ve unzipping daha düzgün bir şekilde yapılabilir, ama sanırım benim durumumda sadece bir kombine alan textintField. Sanırım bunu nasıl yapacağımı biliyorum ama alanları birleştirmek için bir işlev olsaydı, düzgün olurdu.