2012-08-29 1 views
6

Comment générer dynamiquement des formulaires avec un nombre variable de champs de saisie?Génération de formulaire dynamique avec yesod

Le plus proche j'ai réussi est:

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

mais cela a le type de résultat Text et non [Text] comme prévu, posséder à la coïncidence que Text est une instance de Monoid, par exemple il échoue avec Int.

J'ai une tentative de travail alternatif, qui combine plusieurs formes, mais d'une manière ou d'une autre cela ne fonctionne que pour cet exemple de jouet, alors que la tentative réelle échoue étrangement. Quoi qu'il en soit, je ne pense pas que ce soit la bonne approche:

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|] 

Répondre

6

Duh, il utilise réellement facile formes monadiques (voir le code ci-dessous).

Mon principal casse-tête est les champs de texte supplémentaires pour m'assurer que le gestionnaire qui reçoit la réponse peut également déduire la question correspondante. Peut-être que je peux cacher ces champs de texte, les rendre non modifiables, ou trouver un autre moyen de contourner cela (mais je ne connais pas encore beaucoup le code HTML).

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) 

Il y a aussi encore des choses horribles que je n'ai pas la moindre idée, comme emballage toujours le résultat toujours dans un constructeur FormSuccess plus à l'extérieur, mais je suppose que cela dépend vraiment de chaque cas d'utilisation (par exemple, un seul FormFailure ou FormMissing devrait probablement faire échouer toute la forme/manquer, mais peut-être dans certains cas ce n'est pas voulu.)

Tout le zipping et décompresser peut probablement être fait plus proprement, mais je suppose que dans mon cas je viens de créer un champ combiné textintField. Je pense que je sais comment le faire, mais ce serait bien s'il y avait une fonction pour combiner les champs.

Questions connexes