Forms


Clone the code or follow along in the online editor.


Here we will make a rudimentary form. It has a field for your name, a field for your password, and a field to verify that password. We will also do some very simple validation (do the two passwords match?) just because it is simple to add.

The code is a bit longer in this case, but I still think it is valuable to look through it before you get into the description of what is going on.

  1. import Html exposing (..)
  2. import Html.Attributes exposing (..)
  3. import Html.Events exposing (onInput)
  4. main =
  5. Html.beginnerProgram { model = model, view = view, update = update }
  6. -- MODEL
  7. type alias Model =
  8. { name : String
  9. , password : String
  10. , passwordAgain : String
  11. }
  12. model : Model
  13. model =
  14. Model "" "" ""
  15. -- UPDATE
  16. type Msg
  17. = Name String
  18. | Password String
  19. | PasswordAgain String
  20. update : Msg -> Model -> Model
  21. update msg model =
  22. case msg of
  23. Name name ->
  24. { model | name = name }
  25. Password password ->
  26. { model | password = password }
  27. PasswordAgain password ->
  28. { model | passwordAgain = password }
  29. -- VIEW
  30. view : Model -> Html Msg
  31. view model =
  32. div []
  33. [ input [ type_ "text", placeholder "Name", onInput Name ] []
  34. , input [ type_ "password", placeholder "Password", onInput Password ] []
  35. , input [ type_ "password", placeholder "Re-enter Password", onInput PasswordAgain ] []
  36. , viewValidation model
  37. ]
  38. viewValidation : Model -> Html msg
  39. viewValidation model =
  40. let
  41. (color, message) =
  42. if model.password == model.passwordAgain then
  43. ("green", "OK")
  44. else
  45. ("red", "Passwords do not match!")
  46. in
  47. div [ style [("color", color)] ] [ text message ]

This is pretty much exactly how our text field example looked, just with more fields. Let’s walk through how it came to be!

As always, you start out by guessing at the Model. We know there are going to be three text fields, so let’s just go with that:

  1. type alias Model =
  2. { name : String
  3. , password : String
  4. , passwordAgain : String
  5. }

Great, seems reasonable. We expect that each of these fields can be changed separately, so our messages should account for each of those scenarios.

  1. type Msg
  2. = Name String
  3. | Password String
  4. | PasswordAgain String

This means our update is pretty mechanical. Just update the relevant field:

  1. update : Msg -> Model -> Model
  2. update msg model =
  3. case msg of
  4. Name name ->
  5. { model | name = name }
  6. Password password ->
  7. { model | password = password }
  8. PasswordAgain password ->
  9. { model | passwordAgain = password }

We get a little bit fancier than normal in our view though.

  1. view : Model -> Html Msg
  2. view model =
  3. div []
  4. [ input [ type_ "text", placeholder "Name", onInput Name ] []
  5. , input [ type_ "password", placeholder "Password", onInput Password ] []
  6. , input [ type_ "password", placeholder "Re-enter Password", onInput PasswordAgain ] []
  7. , viewValidation model
  8. ]

It starts out normal: We create a <div> and put a couple <input> nodes in it. Each one has an onInput attribute that will tag any changes appropriately for our update function. (This is all building off of the text field example in the previous section.)

But for the last child we do not directly use an HTML function. Instead we call the viewValidation function, passing in the current model.

  1. viewValidation : Model -> Html msg
  2. viewValidation model =
  3. let
  4. (color, message) =
  5. if model.password == model.passwordAgain then
  6. ("green", "OK")
  7. else
  8. ("red", "Passwords do not match!")
  9. in
  10. div [ style [("color", color)] ] [ text message ]

This function first compares the two passwords. If they match, you want green text and a positive message. If they do not match, you want red text and a helpful message. With that info, we produce a <div> filled with a colorful message explaining the situation.

This starts to show the benefits of having our HTML library be normal Elm code. It would have looked really weird to jam all that code into our view. In Elm, you just refactor like you would with any other code!

On these same lines, you may notice that the <input> nodes all are created with pretty similar code. Say we made each input fancier: there is an outer <div> that holds a <span> and an <input> with certain classes. It would make total sense to break that pattern out into a viewInput function so you never have to repeat yourself. This also means you change it in one place and everyone gets the updated HTML.

Exercises: One cool thing about breaking viewValidation out is that it is pretty easy to augment. If you are messing with the code as you read through this (as you should be!) you should try to:

  • Check that the password is longer than 8 characters.
  • Make sure the password contains upper case, lower case, and numeric characters.
  • Add an additional field for age and check that it is a number.
  • Add a “Submit” button. Only show errors after it has been pressed.

Be sure to use the helpers in the String library if you try any of these! Also, we need to learn more before we start talking to servers, so before you try that here, keep reading until HTTP is introduced. It will be significantly easier with proper guidance!