Генерация динамических форм с yesod

Как динамически генерировать формы с различным количеством полей ввода?

Самое близкое, что мне удалось:

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

но это имеет тип результата Text, а не [Text], как предполагалось, из-за совпадения, что Text является экземпляром Monoid, например. он терпит неудачу с 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|]

person Steffen    schedule 29.08.2012    source источник


Ответы (1)


Да, на самом деле легко использовать монадические формы (см. код ниже).

Моя главная головная боль - это дополнительные текстовые поля, чтобы убедиться, что обработчик, который получает ответ, может также сделать вывод о соответствующем вопросе. Может быть, я могу скрыть эти текстовые поля, сделать их недоступными для редактирования или найти другой способ обойти это (но я пока мало знаю о 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. Кажется, я знаю, как это сделать, но было бы здорово, если бы была функция объединения полей.

person Steffen    schedule 30.08.2012