[001.4] Our First HTML Application


Building a basic counter HTML application, using `Html.beginnerProgram`.

Subscribe now

The main focus for Elm at present is to make HTML applications. With that in mind, we'll make our first HTML application. Let's get started.

Project

Getting Started

We'll start off by making a new directory and using elm-package to install elm-html:

mkdir counter
cd counter
elm-package install elm-lang/html

Now we're just going to start off with the most basic starting Elm project - a counter that you can increment or decrement. Let's make our Main.elm file:

vim Main.elm

We'll start off just producing some HTML. To do this, we'll import the Html module to start:

module Main exposing (..)

import Html exposing (..)

Here, we're starting a new module, called Main, and exposing all of it's functions. Then, we're importing everything from the Html module, which is provided by the elm-lang/html package we installed. Let's make a main function that returns some Html:

main =
    div [] [ text "Hello World" ]

This is using the div function from html. All of these functions take two list arguments. The first is a list of attributes, and the second is a list of child elements. In this case we have a div, with no arguments, with a single text node as a child.

Using elm-reactor

We can run this using the elm-reactor for fast development. In the project directory, just run elm-reactor.

elm-reactor

Now you can visit http://localhost:8000 and click on Main.elm to run it. The reactor is a really fast way to develop in Elm.

Adding a Model

Let's make the content an Int and call toString on it, since the text function takes a string argument:

main =
    div [] [ text (toString 1) ]

Alright, so this is a very basic use of html. Let's add a couple of buttons for incrementing and decrementing the counter:

main =
    div []
        [ button [] [ text "-" ]
        , div [] [ text (toString 1) ]
        , button [] [ text "+" ]
        ]

So here we just added a div containing all two action buttons and the counter text. Next, we'll move on to building out the app's functionality.

We'll start off with our Model, which defines the state for our application:

type alias Model =
    Int

Since we just have a counter, we need to track a single Int as our state. This is using a type alias, so we're saying the type for Model is the same as the type for Int.

Adding a Msg type

You can do two things with this model - Increment it and Decrement it. These are our two messages. We'll define a union type for them:

type Msg
    = Increment
    | Decrement

Union Type sounds like something fancy, but it's literally just a few types joined together into a single type.1 In this case, we're saying that a Msg type is either Increment or Decrement.

Building the program

Next, we'll define an update function. This is the first time we're seeing functions, so I'll try to be very explicit. First, we'll define the type signature for the function.

Our update function takes a Msg and a Model and returns a Model. We can describe that with the following type signature:

update : Msg -> Model -> Model

Now we'll define the function. Here we'll define a function that takes 2 arguments, and we'll define a case statement on the message argument:

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

        Decrement ->
            model - 1

Here we're saying that if our message is Increment, we add 1 to the model. If it's Decrement, we subtract 1 from the model. The last expression in a function is the returned value, so here we've satisfied our type signature in that we are just returning an Int, which is all our Model type is.

If you're used to seeing parenthesis and curly braces everywhere when defining functions, this might look a bit weird. The syntax for Elm is extremely sparse, and after a little usage it starts to feel rather natural. I do remember this syntax throwing me for a loop initially though.

OK, so this defines all of the functional operation of our application. We also want to be able to see the model though, so let's do that.

We'll define a function called view. Elm has a very standard way to build applications. Our view function takes a Model and returns a type of HTML that produces Messages (Html Msg).

view : Model -> Html Msg

So here's our view type signature. For now, you can think of it as just taking in a Model and producing some Html, which is a type defined by the elm-lang/html package. There's also the Msg bit, which I'll explain momentarily.

Now we'll copy in the existing main function definition here, replacing the hard-coded 1 with the model argument. We'll define a new main in a moment.

view : Model -> Html Msg
view model =
    div []
        [ button [] [ text "-" ]
        , div [] [ text (toString model) ]
        , button [] [ text "+" ]
        ]

Now for our main function, we're going to use something that Elm provides called beginnerProgram. It is an easy way of wiring a model, update, and view together to make an application. It comes from the html package.

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

We'll look at it in the reactor. It looks like you'd expect - we have our buttons and our model as a string.

The next thing we want to do is start to wire in messages from the buttons. This is where the Msg comes in. In our view function, we want to produce the Increment and Decrement messages when the appropriate buttons are pressed. We do this in the attributes for the buttons:

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

If we try to run it now though, we'll get an error because onClick is not defined. This comes from html's Html.Events module, so let's import this function from it:

import Html.Events exposing (onClick)

Now if you refresh the page, we have a functioning counter with buttons that modify it.

Summary

So that's our first HTML app with Elm. It's pretty fantastically simple, to be honest. The nicest part is this: you now know what Elm applications look like. Every app you see will be a variation on this pattern. See you soon!

Resources


  1. You'll also hear these referred to as Algebraic Data Types, or ADTs 

Published on 05.09.2016