Search code examples
haskelldigestive-functors

How can I validate a field based on another field?


I'm trying to have a registration form with fields like email and emailConfirmation, or password and passwordConfirmation. Validating email and password is easy, there are rules and I've written the respective functions.

The other two are harder though. I found this question and tried writing my code like this:

The form definition, using Blaze:

registrationForm :: (View Html) -> Html
registrationForm view = docTypeHtml $ do
  form ! name "registration" ! method "post" ! action "/register" $ do
    fieldset $ do
      label ! for "password" $ (text "Password")
      inputText "password" view
      errorList "password" view

      br

      label ! for "passwordConfirmation" $ (text "Password Confirmation")
      inputText "passwordConfirmation" view
      errorList "passwordConfirmation" view

and the validator:

data Password = Password { password :: Text }

validateForm :: Monad m => Form Html m Password
validateForm =
  Password
    <$> "password" .: validatePassword
  where
    validatePassword =
      validate fst' $ (,) <$> ("password"             .: D.text Nothing)
                          <*> ("passwordConfirmation" .: D.text Nothing)
    fst' (p1, p2) | p1 == p2  = Success p1
                  | otherwise = Error "Passwords must match"

But whenever I run the server I get a message saying "password is not a field". If I remove the validation and give password a simple validation, then it works as expected. Am I missing something here?


Solution

  • I got help on the digestive-functors repository. Thanks cimmanon

    This is the final code, note field name is now "password.p1"/"password.p2" instead of just password/passwordConfirmation.

    registrationForm :: (View Html) -> Html
    registrationForm view = docTypeHtml $ do
      form ! name "registration" ! method "post" ! action "/register" $ do
        fieldset $ do
          inputText "password.p1" view
          br
          inputText "password.p2" view
          errorList "password" view
    data Password = Password { password :: Text }
    

    validator is the same, except for new names:

    validateForm :: Monad m => Form Html m Password
    validateForm =
      Password
        <$> "password" .: validatePassword
      where
        validatePassword =
          validate fst' $ (,) <$> ("p1" .: D.text Nothing)
                              <*> ("p2" .: D.text Nothing)
        fst' (p1, p2) | p1 == p2  = Success p1
                      | otherwise = Error "Passwords must match"