Text Fields

We are about to create a simple app that reverses the contents of a text field.

Click the blue button to look at this program in the online editor. Try to check out the hint for the type keyword. Click the blue button now!

Edit

  1. import Browser
  2. import Html exposing (Html, Attribute, div, input, text)
  3. import Html.Attributes exposing (..)
  4. import Html.Events exposing (onInput)
  5. -- MAIN
  6. main =
  7. Browser.sandbox { init = init, update = update, view = view }
  8. -- MODEL
  9. type alias Model =
  10. { content : String
  11. }
  12. init : Model
  13. init =
  14. { content = "" }
  15. -- UPDATE
  16. type Msg
  17. = Change String
  18. update : Msg -> Model -> Model
  19. update msg model =
  20. case msg of
  21. Change newContent ->
  22. { model | content = newContent }
  23. -- VIEW
  24. view : Model -> Html Msg
  25. view model =
  26. div []
  27. [ input [ placeholder "Text to reverse", value model.content, onInput Change ] []
  28. , div [] [ text (String.reverse model.content) ]
  29. ]

This code is a slight variant of the previous example. You set up a model. You define some messages. You say how to update. You make your view. The difference is just in how we filled this skeleton in. Let’s walk through that!

Model

I always start by guessing at what my Model should be. We know we have to keep track of whatever the user has typed into the text field. We need that information to know how to render the reversed text. So we go with this:

  1. type alias Model =
  2. { content : String
  3. }

This time I chose to represent the model as a record. The record stores the user input in the content field.

Note: You may be wondering, why bother having a record if it only holds one entry? Couldn’t you just use the string directly? Sure! But starting with a record makes it easy to add more fields as our app gets more complicated. When the time comes where we want two text inputs, we will have to do much less fiddling around.

View

We have our model, so I usually proceed by creating a view function:

  1. view : Model -> Html Msg
  2. view model =
  3. div []
  4. [ input [ placeholder "Text to reverse", value model.content, onInput Change ] []
  5. , div [] [ text (String.reverse model.content) ]
  6. ]

We create a <div> with two children. The interesting child is the <input> node which has three attributes:

  • placeholder is the text that shows when there is no content
  • value is the current content of this <input>
  • onInput sends messages when the user types in this <input> node

Typing in “bard” this would produce four messages:

  1. Change "b"
  2. Change "ba"
  3. Change "bar"
  4. Change "bard"

These would be fed into our update function.

Update

There is only one kind of message in this program, so our update only has to handle one case:

  1. type Msg
  2. = Change String
  3. update : Msg -> Model -> Model
  4. update msg model =
  5. case msg of
  6. Change newContent ->
  7. { model | content = newContent }

When we receive a message that the <input> node has changed, we update the content of our model. So if you typed in “bard” the resulting messages would produce the following models:

  1. { content = "b" }
  2. { content = "ba" }
  3. { content = "bar" }
  4. { content = "bard" }

We need to track this information explicitly in our model, otherwise there is no way to show the reversed text in our view function!

Exercise: Go to the example in the online editor here and show the length of the content in your view function. Use the String.length function!

Note: If you want more info on exactly how the Change values are working in this program, jump ahead to the sections on custom types and pattern matching.