An Introduction to Elm Series: Solution to ‘Forms’ example


In http://guide.elm-lang.org/architecture/user_input/forms.html, you are given a simple form that accepts the name of a user and a password along with a validation field for the password.

You are asked to perform the following validations on the form:

  • 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.

What we did was to add two new members to our model. One for the validation flag (if we should show the result or not) and one for the age. Then we created two new signals, one for the age field and one for the submit button and updated all signals to reset the validation flag. Afterwards, we added on the GUI the new input elements and attached the appropriate signals. Finally, we performed all checks using the appropriate libraries.

In the following proposed full source code solution we highlight our changes.

import Html exposing (..)
import Html.App as Html
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onClick)
import String exposing (trim, length, isEmpty, toLower, toUpper, any, all)
import Char exposing (isDigit, isLower, isUpper)


main =
  Html.beginnerProgram
    { model = model
    , view = view
    , update = update
    }



-- MODEL


type alias Model =
  { name : String
  , password : String
  , passwordAgain : String
  , age : String
  , validate : Bool
  }


model : Model
model =
  Model "" "" "" "" False



-- UPDATE


type Msg
    = Name String
    | Password String
    | PasswordAgain String
    | Age String
    | Validate


update : Msg -> Model -> Model
update msg model =
  case msg of
    Name name ->
      { model | name = name, validate = False }

    Password password ->
      { model | password = password, validate = False }

    PasswordAgain password ->
      { model | passwordAgain = password, validate = False }

    Age age ->
      { model | age = trim age, validate = False }

    Validate ->
      { model | validate = True }

-- VIEW


view : Model -> Html Msg
view model =
  div []
    [ input [ type' "text", placeholder "Name", onInput Name ] []
    , input [ type' "password", placeholder "Password", onInput Password ] []
    , input [ type' "password", placeholder "Re-enter Password", onInput PasswordAgain ] []
    , input [ type' "number", placeholder "Age", onInput Age ] []
    , button [ onClick Validate ] [ text "Submit" ]
    , viewValidation model
    ]


viewValidation : Model -> Html msg
viewValidation model =
  let
    (color, message) =
      if model.validate then
        if length model.password < 8 then
          ("red", "Password too short")
        else if any isUpper model.password == False then
        -- Alternatively, instead of checking if there is at least one character that is Upper, we can convert the input toLower and check if it changed
        -- else if model.password == toLower model.password then
          ("red", "Password does not contain an upper case character")
        else if any isLower model.password == False then
        -- Alternatively, instead of checking if there is at least one character that is lower, we can convert the input toUpper and check if it changed
        -- else if model.password == toUpper model.password then
          ("red", "Password does not contain a lower case character")
        else if any isDigit model.password == False then
          ("red", "Password does not contain a numeric character")
        else if model.password /= model.passwordAgain then
          ("red", "Passwords do not match!")
        else if isEmpty model.age || all isDigit model.age == False then
          ("red", "Age is not a positive integer number")
        else ("green", "OK")
      else ("white", "")
  in
    div [ style [("color", color)] ] [ text message ]

You can download the solution from here 3-form.elm (compressed) (124 downloads)

Leave a Reply