Dynamic Form Generation with Yesod

How can I dynamically generate forms with a different number of input fields?

Closest to me I managed:

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

but it is of the result type Text , and not [Text] as intended, owning the coincidence that Text is an instance of Monoid , for example. he fails with Int .

I have a working alternative attempt that combines several forms, but somehow it only works for this toy example, while the real attempt failed. Anyway, I don't think this is the right approach:

 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 action=@ {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|] 
+6
source share
1 answer

Duh, it’s actually easy to use monadic forms (see code below).

My main headache is additional text fields to make sure that the handler receiving the answer can also output the corresponding question. Maybe I can hide these text fields, make them uneditable, or find another way around this (but I still don’t know much about Html yet).

 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) 

There are also some ugly things that I don’t know about, as always, always ending the result always in the external FormSuccess constructor, but I think it really depends on each use case (for example, one FormFailure or FormMissing should probably make the whole form unsuccessful / absent, but perhaps in some cases this is not necessary.)

All firmware and unpacking, perhaps, can be done more accurately, but I think that in my case I just create a combined textintField field. I think I know how to do this, but it would be neat if there was a function for combining fields.

+6
source

Source: https://habr.com/ru/post/924083/


All Articles