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 Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Html.beginnerProgram { model = model, view = view, update = update }
-- MODEL
type alias Model = Int
model : Model
model =
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 (toString model) ]
, button [ onClick Increment ] [ text "+" ]
]
That’s everything!
Note: This section has
type
andtype 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-lang/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 (toString model) ]
, button [ onClick Increment ] [ text "+" ]
]
You can see more examples of basic HTML here.
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 freedom to make clever 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 theupdate
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!