Buttons


Clone the code or follow along in the online editor.


Our first example is a simple counter that can be incremented or decremented. I find that it can be helpful to see the entire program in one place, so here it is! We will break it down afterwards.

import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)


main =
  Browser.sandbox { init = init, update = update, view = view }


-- MODEL

type alias Model = Int

init : Model
init =
  0


-- UPDATE

type Msg = Increment | Decrement

update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1


-- VIEW

view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (String.fromInt model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]

That's everything!

Note: This section has type and type alias declarations. You can read all about these in the upcoming section on types. You do not need to deeply understand that stuff now, but you are free to jump ahead if it helps.

When writing this program from scratch, I always start by taking a guess at the model. To make a counter, we at least need to keep track of a number that is going up and down. So let's just start with that!

type alias Model = Int

Now that we have a model, we need to define how it changes over time. I always start my UPDATE section by defining a set of messages that we will get from the UI:

type Msg = Increment | Decrement

I definitely know the user will be able to increment and decrement the counter. The Msg type describes these capabilities as data. Important! From there, the update function just describes what to do when you receive one of these messages.

update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1

If you get an Increment message, you increment the model. If you get a Decrement message, you decrement the model. Pretty straight-forward stuff.

Okay, so that's all good, but how do we actually make some HTML and show it on screen? Elm has a library called elm/html that gives you full access to HTML5 as normal Elm functions:

view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (String.fromInt model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]

One thing to notice is that our view function is producing a Html Msg value. This means that it is a chunk of HTML that can produce Msg values. And when you look at the definition, you see the onClick attributes are set to give out Increment and Decrement values. These will get fed directly into our update function, driving our whole app forward.

Another thing to notice is that div and button are just normal Elm functions. These functions take (1) a list of attributes and (2) a list of child nodes. It is just HTML with slightly different syntax. Instead of having < and > everywhere, we have [ and ]. We have found that folks who can read HTML have a pretty easy time learning to read this variation. Okay, but why not have it be exactly like HTML? Since we are using normal Elm functions, we have the full power of the Elm programming language to help us build our views! We can refactor repetitive code out into functions. We can put helpers in modules and import them just like any other code. We can use the same testing frameworks and libraries as any other Elm code. Everything that is nice about programming in Elm is 100% available to help you with your view. No need for a hacked together templating language!

There is also something a bit deeper going on here. The view code is entirely declarative. We take in a Model and produce some Html. That is it. There is no need to mutate the DOM manually. Elm takes care of that behind the scenes. This gives Elm much more leeway to make optimizations and ends up making rendering faster overall. So you write less code and the code runs faster. The best kind of abstraction!

This pattern is the essence of The Elm Architecture. Every example we see from now on will be a slight variation on this basic pattern: Model, update, view.

Exercise: One cool thing about The Elm Architecture is that it is super easy to extend as our product requirements change. Say your product manager has come up with this amazing "reset" feature. A new button that will reset the counter to zero.

To add the feature you come back to the Msg type and add another possibility: Reset. You then move on to the update function and describe what happens when you get that message. Finally you add a button in your view.

See if you can implement the "reset" feature!

results matching ""

    No results matching ""