Search code examples
elm

Extending the Elm tutorial form app to include a numbered input Age


I've been following this tutorial: http://guide.elm-lang.org/architecture/user_input/forms.html

The text there makes sense to me, my question pertains to the exercise it lists at the bottom of the page. It asks that I:

"Add an additional field for age and check that it is a number."

I am having difficulty with this because the onInput function seems to only accept a String input. I find it odd that there is no equivalent for type="number" inputs.

Nevertheless, this is my attempt which does not work:

import Html exposing (..)
import Html.App as Html
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import String exposing (length)

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


-- MODEL

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


model : Model
model =
  Model "" "" "" 0


-- UPDATE

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

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

    Password password ->
      { model | password = password }

    PasswordAgain password ->
      { model | passwordAgain = password }

    Age age ->
      { model | age = age }      


-- 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 ] []
    , viewValidation model
    ]

viewValidation : Model -> Html msg
viewValidation model =
  let
    (color, message) =
      if model.password /= model.passwordAgain then
        ("red", "Passwords do not match!")
      else if length model.password <= 8 then
        ("red", "Password must be more than 8 characters!")
      else
        ("green", "OK")
  in
    div [ style [("color", color)] ] [ text message ]

The error I get is the following:

-- TYPE MISMATCH ----------------------------------------------------- forms.elm

The argument to function `onInput` is causing a mismatch.

58|                                                  onInput Age 
                                                             ^^^
Function `onInput` is expecting the argument to be:

    String -> a

But it is:

    Int -> Msg

Note: I am aware that I could create the Age input as just another text input, but the exercise specifically asked me to check that it is a `number type. I assume this means I should hold it inside the model as an Int.

I am clear about what the error is. I simply want to know the idiomatic way to fix this in Elm. Thanks.


Solution

  • Any user-input from onInput event is a String.

    Your Model expects it to be an Int

    Use String.toInt to parse the integer value from a string value.

    Adjust update function to convert the type to an Int and change the type signature to Age String

    Age age ->
        case String.toInt age of
            Ok val ->
                { model | age = val }
    
            -- Notify the user, or simply ignore the value
            Err err ->
                model
    

    That way you have an option to notify the user about the error.

    In case if Maybe value suits you better, the whole statement can be simplified to:

    Age age ->
        { model | age =  Result.toMaybe (String.toInt age) }