yesodweb / yesod

A RESTful Haskell web framework built on WAI.
http://www.yesodweb.com/
MIT License
2.64k stars 374 forks source link

Yesod.Form.MassInput broken? (Or: How to use it properly?) #1824

Open olafklinke opened 1 year ago

olafklinke commented 1 year ago

Neither the Yesod book not the cookbook examples mention how to use the Yesdod.Form.MassInput.inputList function. It appears that the "Add another row" checkbox does not have the desired effect. Since it is an ordinary input field, for it to have an effect in the absence of any scripts one must either refresh the page (GET) or submit the form (POST). In the latter case, checking the "Add another row" box invariably results in a FormFailure with one message complaining about a required value. In the former case, checking or not any boxes does not change the shape of the form.

From at least version 1.1.4.1 to 1.7.6 the changes in the module source code were cosmetic (updating type signatures) so I dare say the tested version of yesod-form is irrelevant. I tested the code below against stack LTS-16.31 (ghc 8.8.4 and yesod-form-1.6.7) and LTS-19.5 (ghc 9.0.2 and yesod-form-1.7.0) on a 64bit Debian 11 calling the site with Firefox 115.4.0esr.

The following is my minimal working example.

{-# LANGUAGE  OverloadedStrings
             ,QuasiQuotes
             ,TemplateHaskell
             ,TypeFamilies
             ,ViewPatterns
             ,FlexibleContexts
             ,MultiParamTypeClasses #-}
module Main (main) where
import Yesod
import Yesod.Form.I18n.English
import Yesod.Form.MassInput (inputList,massTable)

-- No internal state
data App = App
instance RenderMessage App FormMessage where
    renderMessage _ _ = englishFormMessage

mkYesod "App" [parseRoutes|
/massinput MassInputR GET POST
|]      

instance Yesod App

-- * MassInput 

intListAForm :: AForm Handler [Int]
intListAForm = inputList
    "List of numbers"
    massTable
    (areq intField "Number")
    (Just [0,1])

intListForm :: Html -> MForm Handler (FormResult [Int],Widget)
intListForm = renderDivs intListAForm

getMassInputR :: Handler Html
getMassInputR = do
    ((_,widget),enctype) <- runFormPost intListForm
    defaultLayout $ setTitle "MassInput" >> do
        toWidget [whamlet|
            <form method=post action=@{MassInputR} enctype=#{enctype}>
                ^{widget}
                <button type="submit">Enter
            |]      

postMassInputR :: Handler Html
postMassInputR = do 
    ((result,widget),enctype) <- runFormPost intListForm
    case result of
        FormSuccess theList -> defaultLayout $ setTitle "MassInput" >> do
            toWidget [whamlet|
                <p>State is:
                <p>#{show theList}
                <form method=post action=@{MassInputR} enctype=#{enctype}>
                    ^{widget}
                    <button type="submit">Enter
                |]
        FormMissing -> defaultLayout $ setTitle "FormMissing" >> do
            toWidget [whamlet|
                <p> 
                    <a href=@{MassInputR}>again
                |]
        FormFailure msgs -> defaultLayout $ setTitle "FormFailure" >> do
            toWidget [whamlet|
                $forall txt <- msgs
                    <p>#{txt}
                <p>
                    <a href=@{MassInputR}>again
                |]

main :: IO ()
main = warp 80 App