Javascript Alerts in Yesod - haskell

I have a program where the user can upload a file, some validation of this file takes place, and if the validation fails, I would like to provide feedback to the user via a javascript alert message, rather than via a message embedded in the html itself.
Ideally, once the user has acknowledged the alert message (clicking the alert button), the program can redirect to another route.
Unfortunately the redirection seems to happen right away, without pausing until the user clicks the alert button, so the alert is missed altogether.
Here is a simple snippet which illustrates the problem : the user is asked to pick a file. If it is a text file its name is displayed, otherwise an alert is produced.
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Yesod
import Control.Concurrent.STM (TVar, newTVarIO, readTVarIO, atomically, writeTVar)
import Data.Text (Text)
data App = App (TVar Text)
mkYesod "App" [parseRoutes|
/ HomeR GET POST
/alert AlertR GET
|]
instance Yesod App
instance RenderMessage App FormMessage where
renderMessage _ _ = defaultFormMessage
getHomeR :: Handler Html
getHomeR = do
(formWidget, formEncType) <- generateFormPost uploadForm
App ttxt <- getYesod
txt <- liftIO $ readTVarIO ttxt
liftIO $ print txt
defaultLayout $ do
[whamlet|
<h1>Text file name: #{txt}
<p>
<form method=post action=#{HomeR} enctype=#{formEncType}>
^{formWidget} #
<input type="submit" value="Upload File Name">
|]
postHomeR :: Handler Html
postHomeR = do
((result, _), _) <- runFormPost uploadForm
case result of
FormSuccess fi -> do
app <- getYesod
case fileContentType fi of
"text/plain" -> updateFileName app $ fileName fi
_ -> redirect AlertR
_ -> return ()
redirect HomeR
updateFileName :: App -> Text -> Handler ()
updateFileName app#(App ttxt) txtnew =
liftIO . atomically $ writeTVar ttxt txtnew
getAlertR :: Handler Html
getAlertR = do
defaultLayout $ do
setTitle "ALERT!"
toWidgetBody [julius|
alert("Only text files are accepted");
|]
redirect HomeR
uploadForm = renderDivs $ fileAFormReq "file"
main :: IO ()
main = do
ttxt <- newTVarIO "nil"
warp 3000 $ App ttxt
So this does not work and, in getAlertR, the redirect HomeR code does not "wait" until the user clicks the alert button (in fact the alert is not even displayed).
To get around the issue I have changed getAlertR like that :
getAlertR :: Handler Html
getAlertR = do
defaultLayout $ do
setTitle "ALERT!"
toWidgetBody [julius|
alert("Only text files are accepted");
location.assign("#{HomeR}");
|]
-- redirect HomeR
... which works ok.
But here is my question : is there a more "Yesod-like" way to do this without having the routing inside the julius script?

This is basically an "outside the scope of Yesod" issue: if you want the behavior to occur based on a user responding to an alert box, it has to be handled in Javascript, in which can your approach works quite well. Once within the Javascript world, there are dozens/hundreds of different ways of doing this (automatically using a timer? use a notification message on the page instead of a separate dialog? etc), but there's nothing you can do server side to check that the user has clicked a button without Javascript support.

Related

Yesod.Auth.Email setting password always returns "Passwords did not match, please try again"

Currently trying to set up Yesod.Auth.Email, but running into a bit of a problem after getting the verification url.
On the "Set password" page (with "New password" and "Confirm"), after entering the password and the same in the confirmation box, it always returns "Passwords did not match, please try again".
This is the request that is logged,
POST /auth/page/email/set-password
Params: [("_token","wcpv0LhJfy"),("new","1234"),("confirm","1234")]
Request Body: _token=wcpv0LhJfy&new=1234&confirm=1234
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Status: 303 See Other 0.002459s
Showing that they should indeed match.
Looking into the source code for the handler postPasswordR it's not entirely clear to me why it fails, since this isn't something you overwrite in instance YesodAuthEmail App?
A Minimal Example
Taken straight from the email-auth section in the Yesod book, and saved as Main.hs,
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad (join)
import Control.Monad.Logger (runNoLoggingT)
import Data.Maybe (isJust)
import Data.Text (Text, unpack)
import qualified Data.Text.Lazy.Encoding
import Data.Typeable (Typeable)
import Database.Persist.Sqlite
import Database.Persist.TH
import Network.Mail.Mime
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
import Text.Hamlet (shamlet)
import Text.Shakespeare.Text (stext)
import Yesod
import Yesod.Auth
import Yesod.Auth.Email
share
[ mkPersist
sqlSettings
{ mpsGeneric = False
}
, mkMigrate "migrateAll"
]
[persistLowerCase|
User
email Text
password Text Maybe -- Password may not be set yet
verkey Text Maybe -- Used for resetting passwords
verified Bool
UniqueUser email
deriving Typeable
|]
data App =
App SqlBackend
mkYesod
"App"
[parseRoutes|
/ HomeR GET
/auth AuthR Auth getAuth
|]
instance Yesod App
-- Emails will include links, so be sure to include an approot so that
-- the links are valid!
where
approot = ApprootStatic "http://localhost:3000"
instance RenderMessage App FormMessage where
renderMessage _ _ = defaultFormMessage
-- Set up Persistent
instance YesodPersist App where
type YesodPersistBackend App = SqlBackend
runDB f = do
App conn <- getYesod
runSqlConn f conn
instance YesodAuth App where
type AuthId App = UserId
loginDest _ = HomeR
logoutDest _ = HomeR
authPlugins _ = [authEmail]
-- Need to find the UserId for the given email address.
getAuthId creds =
runDB $
do x <- insertBy $ User (credsIdent creds) Nothing Nothing False
return $
Just $
case x of
Left (Entity userid _) -> userid -- newly added user
Right userid -> userid -- existing user
authHttpManager = error "Email doesn't need an HTTP manager"
instance YesodAuthPersist App
-- Here's all of the email-specific code
instance YesodAuthEmail App where
type AuthEmailId App = UserId
afterPasswordRoute _ = HomeR
addUnverified email verkey =
runDB $ insert $ User email Nothing (Just verkey) False
sendVerifyEmail email _ verurl
-- Print out to the console the verification email, for easier
-- debugging.
= do
liftIO $ putStrLn $ "Copy/ Paste this URL in your browser:" ++ unpack verurl
-- Send email.
liftIO $
renderSendMail
(emptyMail $ Address Nothing "noreply")
{ mailTo = [Address Nothing email]
, mailHeaders = [("Subject", "Verify your email address")]
, mailParts = [[textPart, htmlPart]]
}
where
textPart =
Part
{ partType = "text/plain; charset=utf-8"
, partEncoding = None
, partFilename = Nothing
, partContent =
Data.Text.Lazy.Encoding.encodeUtf8
[stext|
Please confirm your email address by clicking on the link below.
#{verurl}
Thank you
|]
, partHeaders = []
}
htmlPart =
Part
{ partType = "text/html; charset=utf-8"
, partEncoding = None
, partFilename = Nothing
, partContent =
renderHtml
[shamlet|
<p>Please confirm your email address by clicking on the link below.
<p>
<a href=#{verurl}>#{verurl}
<p>Thank you
|]
, partHeaders = []
}
getVerifyKey = runDB . fmap (join . fmap userVerkey) . get
setVerifyKey uid key = runDB $ update uid [UserVerkey =. Just key]
verifyAccount uid =
runDB $
do mu <- get uid
case mu of
Nothing -> return Nothing
Just u -> do
update uid [UserVerified =. True]
return $ Just uid
getPassword = runDB . fmap (join . fmap userPassword) . get
setPassword uid pass = runDB $ update uid [UserPassword =. Just pass]
getEmailCreds email =
runDB $
do mu <- getBy $ UniqueUser email
case mu of
Nothing -> return Nothing
Just (Entity uid u) ->
return $
Just
EmailCreds
{ emailCredsId = uid
, emailCredsAuthId = Just uid
, emailCredsStatus = isJust $ userPassword u
, emailCredsVerkey = userVerkey u
, emailCredsEmail = email
}
getEmail = runDB . fmap (fmap userEmail) . get
getHomeR :: Handler Html
getHomeR = do
maid <- maybeAuthId
defaultLayout
[whamlet|
<p>Your current auth ID: #{show maid}
$maybe _ <- maid
<p>
<a href=#{AuthR LogoutR}>Logout
$nothing
<p>
<a href=#{AuthR LoginR}>Go to the login page
|]
main :: IO ()
main =
runNoLoggingT $
withSqliteConn "email.db3" $
\conn ->
liftIO $
do runSqlConn (runMigration migrateAll) conn
warp 3000 $ App conn
It is then run with stack runghc Main.hs after having installed stack install yesod persistent-sqlite.
My stackage LTS version is lts-7.14, and I'm running GHC version 8.0.1.20161117.
Still getting the same result.
You will have to enable the CSRF middleware to make it work properly. I will update the book example accordingly. The error message is obviously not good and it should be improved. To make your code work, add this while making the instance of Yesod typeclass with the foundation type:
yesodMiddleware = defaultCsrfMiddleware . defaultYesodMiddleware

How add bootstrap to YesodForm

I need Help to add BootStrap to My YesodForm Project. Can you help me, guys ?
Thats my code. I just want add BootStrap styles to add to my html components
I already read aloot tutorials but are complexive, i am new at Haskell. I Need a simples thing just to that simple project. Thank.s
{-# LANGUAGE OverloadedStrings, TypeFamilies, QuasiQuotes,
TemplateHaskell, GADTs, FlexibleInstances,
MultiParamTypeClasses, DeriveDataTypeable,
GeneralizedNewtypeDeriving, ViewPatterns, EmptyDataDecls #-}
import Yesod
import Database.Persist.Postgresql
import Data.Text
import Text.Lucius
import Control.Monad.Logger (runStdoutLoggingT)
data Pagina = Pagina{connPool :: ConnectionPool}
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Animals json
nome Text
idade Int
deriving Show
Users json
nome Text
login Text
senha Text
deriving Show
|]
mkYesod "Pagina" [parseRoutes|
/ HomeR GET
/animal/cadastro AnimalR GET POST
/animal/checar/#AnimalsId ChecarAnimalR GET
/erro ErroR GET
/login LoginR GET POST
/usuario UsuarioR GET POST
/perfil/#UsersId PerfilR GET
/admin AdminR GET
/logout LogoutR GET
|]
instance Yesod Pagina where
authRoute _ = Just LoginR
isAuthorized LoginR _ = return Authorized
isAuthorized ErroR _ = return Authorized
isAuthorized HomeR _ = return Authorized
isAuthorized UsuarioR _ = return Authorized
isAuthorized AdminR _ = isAdmin
isAuthorized _ _ = isUser
isUser = do
mu <- lookupSession "_ID"
return $ case mu of
Nothing -> AuthenticationRequired
Just _ -> Authorized
isAdmin = do
mu <- lookupSession "_ID"
return $ case mu of
Nothing -> AuthenticationRequired
Just "admin" -> Authorized
Just _ -> Unauthorized "Acesso Restrito para Administrador"
instance YesodPersist Pagina where
type YesodPersistBackend Pagina = SqlBackend
runDB f = do
master <- getYesod
let pool = connPool master
runSqlPool f pool
type Form a = Html -> MForm Handler (FormResult a, Widget)
instance RenderMessage Pagina FormMessage where
renderMessage _ _ = defaultFormMessage
------------------------
formAnimal :: Form Animals
formAnimal = renderDivs $ Animals <$>
areq textField "Nome: " Nothing <*>
areq intField "Idade: " Nothing
formUser :: Form Users
formUser = renderDivs $ Users <$>
areq textField "Nome: " Nothing <*>
areq textField "Login: " Nothing <*>
areq passwordField "Password: " Nothing
formLogin :: Form (Text,Text)
formLogin = renderDivs $ (,) <$>
areq textField "Login: " Nothing <*>
areq passwordField "Senha: " Nothing
getAnimalR :: Handler Html
getAnimalR = do
(widget, enctype) <- generateFormPost formAnimal
defaultLayout $ do
toWidget [cassius|
label
color:blue;
|]
[whamlet|
<form .form-horizontal method=post enctype=#{enctype} action=#{AnimalR}>
^{widget}
<input type="submit" value="Cadastrar Animal">
|]
getPerfilR :: UsersId -> Handler Html
getPerfilR uid = do
user <- runDB $ get404 uid
defaultLayout $ do
toWidget $ $(luciusFile "templates/perfil.lucius")
$(whamletFile "templates/perfil.hamlet")
getUsuarioR :: Handler Html
getUsuarioR = do
(widget, enctype) <- generateFormPost formUser
defaultLayout [whamlet|
<form method=post enctype=#{enctype} action=#{UsuarioR}>
^{widget}
<input type="submit" value="Enviar">
|]
postAnimalR :: Handler Html
postAnimalR = do
((result, _), _) <- runFormPost formAnimal
case result of
FormSuccess anim -> (runDB $ insert anim) >>= \piid -> redirect (ChecarAnimalR piid)
_ -> redirect ErroR
postUsuarioR :: Handler Html
postUsuarioR = do
((result, _), _) <- runFormPost formUser
case result of
FormSuccess user -> (runDB $ insert user) >>= \piid -> redirect (PerfilR piid)
_ -> redirect ErroR
getHomeR :: Handler Html
getHomeR = defaultLayout [whamlet|Hello World!|]
addStyle :: Widget
addStyle = addStylesheetRemote "http://netdna.bootstrapcdn.com/twitter-bootstrap/2.1.0/css/bootstrap-combined.min.css"
getAdminR :: Handler Html
getAdminR = defaultLayout [whamlet|
<b><h1><font size="11"> Bem vindo ao Painel Administrativo</font></h1></b>
|]
getLoginR :: Handler Html
getLoginR = do
(widget, enctype) <- generateFormPost formLogin
defaultLayout [whamlet|
<form method=post enctype=#{enctype} action=#{LoginR}>
^{widget}
<input type="submit" value="Login">
|]
postLoginR :: Handler Html
postLoginR = do
((result, _), _) <- runFormPost formLogin
case result of
FormSuccess ("admin","eitapleuga") -> setSession "_ID" "admin" >> redirect AdminR
FormSuccess (login,senha) -> do
user <- runDB $ selectFirst [UsersLogin ==. login, UsersSenha ==. senha] []
case user of
Nothing -> redirect LoginR
Just (Entity pid u) -> setSession "_ID" (pack $ show $ fromSqlKey pid) >> redirect (PerfilR pid)
getChecarAnimalR :: AnimalsId -> Handler Html
getChecarAnimalR pid = do
animal <- runDB $ get404 pid
defaultLayout [whamlet|
<font size="10">Perfil do Pet</font><br>
<p><b> Nome do Pet:</b> #{animalsNome animal}
<p><b> Idade do Pet:</b> #{show $ animalsIdade animal} Anos
|]
getErroR :: Handler Html
getErroR = defaultLayout [whamlet|
<h1>Falha no Cadastro !</h1>
|]
getLogoutR :: Handler Html
getLogoutR = do
deleteSession "_ID"
defaultLayout [whamlet|
<h1> <b>Logout</b> efetuado com sucesso! </h1>
|]
connStr = "dbname=d4673as0stmsm7 host=ec2-54-221-225-242.compute-1.amazonaws.com user=nzjfptmglfomng password=fyYms4A9T8gkP4_Go8GswcfIiE port=5432"
main::IO()
main = runStdoutLoggingT $ withPostgresqlPool connStr 10 $ \pool -> liftIO $ do
runSqlPersistMPool (runMigration migrateAll) pool
warp 8080 (Pagina pool)
You can add it in your Handler function like this:
getUsuarioR :: Handler Html
getUsuarioR = do
(widget, enctype) <- generateFormPost formUser
defaultLayout $ do
addStylesheetRemote "http://remote-bootstrap-path.css"
[whamlet|
<form method=post enctype=#{enctype} action=#{UsuarioR}>
^{widget}
<input type="submit" value="Enviar">
|]
Also if you are new to Haskell, I would suggest you to learn some minimal Haskell before diving into Yesod.
Especially for a first project I would advise to use the scaffolded site. You can get it by following the quick start guide on the Yesod home page. Not only do you get sensible default settings, but it also has the bootstrap css already integrated.
The downside is of course that the scaffolding throws a lot of stuff and opinions at you that you might not want or not agree with. But even if you don't like the scaffolding, you can always keep one in a separate folder and just take inspiration from it and incorporate parts of it into your own site - like the integration of bootstrap or jquery.
What you need to do next is to add the appropriate classes to your html elements. There are two ways to do that, depending on the context. In your own widgets you just add them like you would any other class (with a point in front of it). If you use code that is generated, for example for forms or for yesod-table, you often have a choice between different rendering functions. For example you can render forms in two columns by using the renderTable rendering function. But there is almost always another function with a name like renderBootstrap which will render the content as plain divs with bootstrap classes.
So all in all Yesod is extremely well integrated with bootstrap. Even too much in my personal opinion. But in your case it should make your task comparatively easy after the initial setup confusion.

How to write Yesod form that checks if two fileds are the same?

Let's say we have something like this:
myForm :: Form (Text, Text)
myForm = renderBootstrap3 BootstrapBasicForm $ (,)
<$> areq passwordField (bfs ("Password" :: Text)) Nothing
<*> areq passwordField (bfs ("Repeat password" :: Text)) Nothing
Is it possible to check whether the both field are the same? Validation is
described here,
check
seems to be not powerful enough to perform this sort of check. Maybe
checkM
may be of some use?
If it's not possible to do with built-in Yesod facilities, what would be the
best work-around? I can think of:
postSomethingR :: Handler Html
postSomethingR = do
((result, form), enctype) <- runFormPost myForm
case result of
FormSuccess (password0, password1) -> do
if password0 == password1
then
-- do your thing
else
-- serve the form again and perhaps set message telling that
-- passwords don't match?
This is a working example of a custom password field that checks if the input from both boxes were the same. This comparison is created in the record fieldParse.
To run this example from cmd: stack runghc <filename.hs>
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Applicative
import Data.Text (Text)
import Yesod
data App = App
mkYesod "App" [parseRoutes|
/ HomeR GET
|]
instance Yesod App
instance RenderMessage App FormMessage where
renderMessage _ _ = defaultFormMessage
passwordConfirmField :: Field Handler Text
passwordConfirmField = Field
{ fieldParse = \rawVals _fileVals ->
case rawVals of
[a, b]
| a == b -> return $ Right $ Just a
| otherwise -> return $ Left "Passwords don't match"
[] -> return $ Right Nothing
_ -> return $ Left "You must enter two values"
, fieldView = \idAttr nameAttr otherAttrs eResult isReq ->
[whamlet|
<input id=#{idAttr} name=#{nameAttr} *{otherAttrs} type=password>
<div>Confirm:
<input id=#{idAttr}-confirm name=#{nameAttr} *{otherAttrs} type=password>
|]
, fieldEnctype = UrlEncoded
}
getHomeR :: Handler Html
getHomeR = do
((res, widget), enctype) <- runFormGet $ renderDivs $
areq passwordConfirmField "Password" Nothing
defaultLayout
[whamlet|
<p>Result: #{show res}
<form enctype=#{enctype}>
^{widget}
<input type=submit value="Change password">
|]
main :: IO ()
main = warp 3000 App

Passing Label Values to a Form as Text/String

This is a follow-up to my question here: Extracting database field values inside a Handler
I would like to extract some information from the database and pass it in as a label value for a form. However, I get a type error.
A simple demonstration below (shell code from the Yesod Book):
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Applicative ((<$>), (<*>))
import Data.Text (Text)
import Data.Time (Day)
import Yesod
import Yesod.Form.Jquery
data App = App
mkYesod "App" [parseRoutes|
/ HomeR GET
|]
instance Yesod App
instance RenderMessage App FormMessage where
renderMessage _ _ = defaultFormMessage
instance YesodJquery App
data Person = Person
{ personName :: Text
}
deriving Show
personForm :: Text -> Html -> MForm Handler (FormResult Person, Widget)
personForm n1 = renderDivs $ Person
<$> areq textField n1 Nothing -- Changing n1 to "Name" works just fine.
getHomeR :: Handler Html
getHomeR = do
(widget, enctype) <- generateFormPost $ personForm "test"
defaultLayout
[whamlet|
<p>
The widget generated contains only the contents
of the form, not the form tag itself. So...
|]
main :: IO ()
main = warp 3000 App
When I run the program with runhaskell, I get the following error:
Couldn't match expected type `FieldSettings site0'
with actual type `Text'
In the second argument of `areq', namely `n1'
In the second argument of `(<$>)', namely
`areq textField n1 Nothing'
In the second argument of `($)', namely
`Person <$> areq textField n1 Nothing'
I also tried (FieldSettings n1 Nothing Nothing Nothing []) but no luck. Any thoughts on how to pass in label values to areq?
Let us look at the type of areq
areq :: (RenderMessage master msg, RenderMessage master FormMessage) => Field sub master a -> FieldSettings msg -> Maybe a -> AForm sub master a
So the areq function doesn't expect a Text value as its second parameter, it expects a FieldSettings msg. So what is the reason that it works when you write "Name" in your source code?
Looking up the documentation for FieldSettings in the documentation for yesod-forms we see that it is a datatype that has an IsString instance. Looking at the specific instance in the source code
we see that:
instance (a ~ Text) => IsString (FieldSettings a) where
fromString s = FieldSettings (fromString s) Nothing Nothing Nothing []
So everytime you write a String in your source code (if the OverloadedStrings extension is enabled), the compiler will insert the contents of the fromString instance.
However, you don't want to enter a String, but instead you want to create a FieldSettings from a Text value. Looking further we see that the first part of a FieldSettings is a SomeMessage, checking the documentation yet again and the source we see that a SomeMessage can be created by using the SomeMessage constructor.
personForm :: Text -> Html -> MForm Handler (FormResult Person, Widget)
personForm n1 = renderDivs $ Person
<$> areq textField (FieldSettings (SomeMessage n1) Nothing Nothing Nothing []) Nothing

how to use monadic forms?

I am implementing a "contact me" form that will send an email when it is submitted. I needed this form to emit custom HTML, so I ended up using monadic forms. The problem is that I do not know how to use a monadic form.
the code is below. I have omitted the part that sends e-mail for brevity. the problem is that my form never validates correctly. the form result is never FormSuccess in my postContactR function.
It seems that I do not initialize the form correctly when I call runFormPost inside postContactR. I always pass Nothing instead of the actual ContactData to contactForm and I do not know how to construct my ContactData from the request. Is my understanding of the problem correct? I am trying to work with poorly documented features. :)
any help?
EDIT: what looks strange is that validation errors do show up in the form if I submit an invalid form, so the request data does get read at some point. what does not work is that when there are no errors I do not get redirected to RootR
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
module Handler.Contact where
import Control.Applicative ((<$>), (<*>))
import Data.Text (Text)
import Foundation
import Network.Mail.Mime
data ContactData = ContactData
{ contactName :: Text
, contactEmail :: Text
, contactMessage :: Textarea
}
deriving Show
contactForm d = \html -> do
(r1, v1) <- mreq textField "Your name:" (contactName <$> d)
(r2, v2) <- mreq emailField "Your e-mail:" (contactEmail <$> d)
(r3, v3) <- mreq textareaField "Message:" (contactMessage <$> d)
let views = [v1, v2, v3]
return (ContactData <$> r1 <*> r2 <*> r3, $(widgetFile "contact-form"))
getContactR :: Handler RepHtml
getContactR = do
((_, form), _) <- runFormPost (contactForm Nothing)
defaultLayout $ do
setTitle "contact"
addWidget $(widgetFile "contact")
postContactR :: Handler RepHtml
postContactR = do
((r, form), _) <- runFormPost (contactForm Nothing)
case r of
FormSuccess d -> do
sendEmail d
setMessage "Message sent"
redirect RedirectTemporary RootR
_ -> getContactR
Are you including the html value in contact-form.hamlet? It's a nonce value. You'd get better debug information if you printed the value of r (in postContactR).
I have on my writing TODO list to add a monadic form example, it should be up soon.

Resources