title: Dynamic form generation with yesod
body: <p>How do I dynamically generate forms with a varying number of input fields?</p>
  
  <p>The closest I managed is:</p>
  
  <pre><code>listEditForm :: [String] -&gt; Html -&gt; MForm App App (FormResult Text, Widget)
  listEditForm xs = renderDivs $ mconcat [ areq textField (String.fromString x) Nothing | x &lt;- xs]
  </code></pre>
  
  <p>but this has the result type <code>Text</code> and not <code>[Text]</code> as intended, owning to the coincidence that <code>Text</code> is an instance of <code>Monoid</code>, e.g. it fails with <code>Int</code>.</p>
  
  <p>I have a working alternate attempt, which combines several forms, but somehow it only works for this toy example, while the real attempt fails strangely. Anyhow, I don't think this is the correct approach:</p>
  
  <pre><code>data MapPair = MapPair { mpKey :: T.Text, mpValue :: Maybe T.Text }
  
  editForm mmp = renderTable $ MapPair
    &lt;$&gt; areq textField "Key"   (mpKey  &lt;$&gt; mmp)
    &lt;*&gt; aopt textField "Value" (mpValue &lt;$&gt; mmp)
  
  pair2mp (v,k) = MapPair { mpKey = v, mpValue = Just k }
  
  getEditR = do
    sess &lt;- getSession
    let sesslist = Map.toList $ Map.map (decodeUtf8With lenientDecode) sess  
    forms &lt;- forM sesslist (\a -&gt; generateFormPost $ editForm $ Just $ pair2mp a)
  
    defaultLayout [whamlet|
      &lt;h1&gt;Edit Value Pairs
      $forall (widget,enctype) &lt;- forms
        &lt;form method=post action=@{EditR} enctype=#{enctype}&gt;
          ^{widget}
          &lt;input type=submit&gt;
    |]
  
    postEditR = do
      sess &lt;- getSession
      let sesslist = Map.toList $ Map.map (decodeUtf8With lenientDecode) sess
      forM_ sesslist (\a -&gt; do
          ((res,_),_) &lt;- runFormPost $ editForm $ Just $ pair2mp a
          case res of
            (FormSuccess (MapPair {mpKey=mk, mpValue=(Just mv)})) -&gt; setSession mk mv
            _ -&gt; return ()
        )
      defaultLayout [whamlet|ok|]
  </code></pre>