Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

elm

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.

like image 308
adrianmc Avatar asked Aug 16 '16 18:08

adrianmc


2 Answers

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) }
like image 68
halfzebra Avatar answered Sep 24 '22 03:09

halfzebra


You'll need the equivalent of onInput but for one that operates on integers. Based on how targetValue is defined, you can do something similar with the addition of Json.Decode.int to parse it as an integer:

onIntInput : (Int -> msg) -> Attribute msg
onIntInput tagger =
  Html.Events.on "input" (Json.map tagger (Json.at ["target", "value"] Json.int))

You can then use it as such:

, input [ type' "number", placeholder "Age", onIntInput Age ] []
like image 24
Chad Gilbert Avatar answered Sep 22 '22 03:09

Chad Gilbert