I would like to provide some context for the widgets (Html Forms) that are being generated by runFormPost
. I thought I can simply stick the result into a Tuple with my context and pattern match it in my Hamlet but it turning out to be challenging because of Handler Monads.
I have a Monadic form with the following type signature:
myForm :: ModelId -> Model -> Html -> MForm Handler (FormResult MyData, Widget)
myForm rid rec extra = do
-- whamlet code here
I render a series of forms inside forM
and use the list of widgets in my Hamlet file. It all works fine.
widgets <- forM rs' $ \(Entity rid rec) ->
runFormPost $ myForm rid rec
Now, I would like to add some data to each widget and return it as a tuple. For this simple example, let's assume I want to add a String. I tried the following code and it does not compile when I try to use it in my Hamlet file (it compiles if I don't use widgets
list in my Hamlet file)
widgets <- forM rs' $ \(Entity rid rec) ->
return ("Test", runFormPost $ myForm rid rec)
In my Hamlet file, I tried something like this (x
is my String context):
$forall (x,((res,widget), enctype)) <- widgets
<div>
<form method=post action=@{HandlerR hId} enctype=#{enctype}>
^{widget}
I get the following error:
Couldn't match expected type `((t0, a1), a0)'
with actual type `Handler ((FormResult MyData, Widget), Enctype)'
Expected type: [(t1, ((t0, a1), a0))]
Actual type: [(t1,
Handler ((FormResult MyData, Widget), Enctype))]
In the second argument of `Data.Foldable.mapM_', namely `widgets'
So far I have tried using fmap
and liftM
inside forM or map
but I keep getting a similar error. I also tried throwing Handler
in my pattern match which gave me an error saying Handler
not in scope.
Any thoughts on how I can append some additional piece of info to a widget and re-use it in my Hamlet file?
Thanks!
The problem is that runFormPost
yields a result which is wrapped in a monad, Handler
in this case.
widgets :: (String, Handler ((FormResult MyData, Widget), Enctype)
So, you should use the unwrapped version widgets which you defined at first, to add your additional information. Using your first version of widgets :: ((FormResult MyData, Widget), Enctype)
, you can then add the line
let widgetsWithInfo = map (\w -> ("test", w)) widgets
In your hamlet file, you should then embed widgetsWithInfo :: (String, ((FormResult MyData, Widget), Enctype)
.
To your additional question: Your intention with fmap
seems right to me, but (if it's not just a typo) your expression is parenthesised incorrect. You want to apply fmap
to the result of runFormPost $ myForm rid rec
, so you have to parenthesise that subexpression:
fmap (\x -> ("Test", x)) (rumformPost $ myForm rid rec)