Maintaining Applicative Form Behavior In Monadic Forms
September 2, 2019 ยท View on GitHub
Applicative forms have a number of useful features that you lose when switching to Monadic forms, such as generating labels, displaying errors beside the input field, etc, any of which you would then have to re-implement yourself in the monadic context.
However, you can separate the overall form into individual applicative forms, one per field, and then embed them within a monadic form, you can maintain the individual behavior.
Here is an example:
joinForm :: Html -> MForm Handler (FormResult Join, Widget)
joinForm extra = do
let
mustAgreeField :: Text -> Field Handler Bool
mustAgreeField errorMsg = checkBool id errorMsg checkBoxField
accept1Def = areq (mustAgreeField
"You must specify that you accept Section 1")
"Accept Section 1" Nothing
accept2Def = areq (mustAgreeField
"You must specify that you accept Section 2")
"Accept Section 2" Nothing
fullNameDef = areq
(checkBool
((>0) . length)
("You must include your full, legal name" :: Text)
textField)
"Full Name"
Nothing
(accept1, accept1Widget) <- renderDivs accept1Def mempty
(accept2, accept2Widget) <- renderDivs accept2Def mempty
(fullName, fullNameWidget) <- renderDivs fullNameDef mempty
let
widget =
[whamlet|
^{extra}
<p.my-4> this is a sentence
^{accept1Widget}
<p.my-4> a sep
^{accept2Widget}
<p.my-4> another sep
^{fullNameWidget}
|]
joinRes = Join <$> accept1 <*> accept2 <*> fullName
return (joinRes, widget)
data Join = Join
{ joinSignedSection1 :: Bool
, joinSignedSection2 :: Bool
, joinFullName :: Text
}
deriving Show
- each applicative field is converted to the monadic context via
renderDivs memptyis passed torenderDivsinstead of the usualextra, andextrais embedded in the parent widget. Otherwise, CSRF will be included multiple times, and this will not work