使用yesod生成动态表单

时间:2012-08-29 14:07:16

标签: haskell yesod

如何使用不同数量的输入字段动态生成表单?

我最接近的是:

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

但是它的结果类型为Text,而不是[Text],因为TextMonoid的实例的巧合,例如它以Int失败。

我有一个工作的替代尝试,它结合了几种形式,但不知何故它只适用于这个玩具示例,而真正的尝试失败了奇怪。无论如何,我认为这不是正确的方法:

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

1 个答案:

答案 0 :(得分:6)

Duh,使用monadic表单实际上很容易(参见下面的代码)。

我最头疼的是额外的文本字段,以确保接收答案的处理程序也可以推断出相应的问题。也许我可以隐藏那些文本字段,使它们不可编辑,或者找到另一种方法(但我对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)

还有一些我不知道的丑陋的东西,比如总是将结果总是包装在最外层的FormSuccess构造函数中,但我想这实际上取决于每个用例(例如单个FormFailure或FormMissing应该可能使整个表单失败/丢失,但在某些情况下可能不需要。)

所有的压缩和解压缩都可以更整齐地完成,但我想在我的情况下我只创建一个组合字段textintField。我想我知道怎么做,但如果有一个组合字段的功能,它会很简洁。