AFormとMForm
YesodにはAFormとMFormがある、
「あぷりかてぃぶふぉーむ」と
「モナドふぉーむ」らしいが、名前をみても何が違うのが分からないのがYesod...簡単にいうと、AFormは「お手軽版」でMFormが「好きにやれよ版」。
だと思ってる...
Formの情報をhaskellの型へ流し込んだり、その逆をやったりするもの、Formをいじくる関数を定義しておいて、generateFormPost関数とかでフォームを作成する、なんでGetとかPostでフォームの作成関数が違うのかは、CSRFとかのタグの出力があるとかないとかでちがいがある。
サンプルはこちらに置いときます。
AForm
ほとんどの場合が、これで済みそうな感じ、普通のフォームを上から並べてくならAFormで十分。
ただ、renderDivs関数とかでくるむ必要があるので、ちょっと細工をしたい場合に、邪魔になるケースがある、多分こでが気に入らなかったらMForm使えってことかな。
その他に、 renderDivsNoLabels関数とかrenderTable関数とかある、サンプルはrenderBootstrap3をBootStrap用の3種類のフォームで出力してみた。
-- Formの情報がここにはいる
data FileForm = FileForm {
fileInfo :: FileInfo
, inpText :: Text
}
fileUploadAFormInline :: Form FileForm
fileUploadAFormInline = renderBootstrap3 BootstrapInlineForm $ getAform
fileUploadAFormBasic :: Form FileForm
fileUploadAFormBasic = renderBootstrap3 BootstrapBasicForm $ getAform
fileUploadAFormHorizon :: Form FileForm
fileUploadAFormHorizon = renderBootstrap3 (BootstrapHorizontalForm (ColXs 2) (ColXs 8) (ColXs 2) (ColXs 6)) $ getAform
getAform :: AForm Handler FileForm
getAform = FileForm
<$> fileAFormReq fs <*> areq textField tfs Nothing
where fs = FieldSettings {
fsLabel = "ファイル"
, fsTooltip = Nothing
, fsId = Nothing
, fsName = Just "media"
, fsAttrs =
[ ("class", "form-group")
, ("placeholder", "File description")
]
}
tfs = FieldSettings {
fsLabel = "なんかいれろ"
, fsTooltip = Nothing
, fsId = Just "inptext"
, fsName = Just "inptext"
, fsAttrs =
[ ("class", "form-group form-control")
, ("placeholder", "input text")
]
}
フォームの情報を取得する変数を<$>と<*>でいれていく、Formコントロールにスタイル等を設定したい場合は、FieldSettingsで属性情報を追加する。
でこれをHandlerで走らせて、Widgetを取得する
getFormexamR :: Handler Html
getFormexamR = do
(formAWidgetBasic, formAEnctypeBasic) <- generateFormPost fileUploadAFormBasic
(formAWidgetInline, formAEnctypeInline) <- generateFormPost fileUploadAFormInline
(formAWidgetHorizon, formAEnctypeHorizon) <- generateFormPost fileUploadAFormHorizon
((res, widget), enctype) <- runFormPost fileUploadMForm
defaultLayout $ do
setTitle "Form Example!!!"
$(widgetFile "formexam")
MForm
こっちは細かくフォームを作成できる方、mreqでつなげるぐらいで、基本的にAFormと一緒、mreqでFormから値を取り出すときに、ResultとViewが別々にとれるので、手動でFileFormへつめてやるところぐらいが違う、HTMLを細かく調整できる。
fileUploadMForm :: Html -> MForm Handler (FormResult FileForm, Widget)
fileUploadMForm extra = do
let fsMedia = FieldSettings {
fsLabel = ""
, fsTooltip = Nothing
, fsId = Nothing
, fsName = Just "media"
, fsAttrs =
[ ("class", "uploadFile")
, ("style", "display:none")
, ("placeholder", "")
]
}
let fsText = FieldSettings {
fsLabel = "なんか"
, fsTooltip = Nothing
, fsId = Nothing
, fsName = Just "inptext"
, fsAttrs =
[ ("class", "form-group form-control")
, ("placeholder", "")
]
}
(fileInfoRes, fileInfoView) <- mreq fileField fsMedia Nothing
(inpTextRes, inpTextView) <- mreq textField fsText Nothing
let fileRes = FileForm <$> fileInfoRes <*> inpTextRes
let widget = do
[whamlet|
#{extra}
<label>
<div class="form-group">
<span class="btn btn-default">Upload
^{fvInput fileInfoView}
^{fvInput inpTextView}
|]
return (fileRes, widget)
これをHandlerでつかう、resにはFileFormでwidgetにはwhamletで作成したwidgetがはいる。
...
((res, widget), enctype) <- runFormPost fileUploadMForm
...
出力はこんな感じ
HTMLの出力は4種類出力してみましたが、BootstrapとHTMLの知識は皆無ですので、あまり突っ込まないでください。