Yesod で1ページに複数個フォームがある場合は identifyForm を使う

resolver lts-5.4 で確認。

問題

1ページに複数個フォームがある場合、runFormPost はそれぞれのフォームの区別をしてくれません。どういうことかというと、例えば下記のような2つのフォームを利用するとします。

data AFormData = AFormData Text
aForm :: Html -> MForm Handler (FormResult AFormData, Widget)
aForm = renderDivs $ AFormData <$> areq textField "A Text" Nothing

data BFormData = BFormData Text
bForm :: Html -> MForm Handler (FormResult BFormData, Widget)
bForm = renderDivs $ BFormData <$> areq textField "B Text" Nothing

この2つのフォームを1つのページに配置し、ブラウザーでアクセスしどちらか一方のフォームに “Hello” とでも入力し送信します。すると runFormPost aFormrunFormPost bForm も返り値の FormResult Xxx 型の部分が FormSuccess xxx となり、xxx の部分はそれぞれ AFormData "Hello"BFormData "Hello" となります。

これは困ります。

解決

identifyForm を使います。

identifyForm
  :: Monad m
  => Text -- ^ Form identification string.
  -> (Html -> MForm m (FormResult a, WidgetT (HandlerSite m) IO ()))
  -> (Html -> MForm m (FormResult a, WidgetT (HandlerSite m) IO ()))

第1引数にユニークな文字列を指定してください。

aForm = identifyForm "a-form" $ renderDivs $ AFormData <$> areq textField "A Text" Nothing

これで、対応するフォームに入力されたときだけ runFormPostFormSuccess を返すようになります。

参考

Issue yesod-web/yesod #649