Web Development

In this post we will look at how to build Web Applications using Elm. You will get to see how the functional style of programming allows us to build web applications that are more "composable". We will use Signals, Tasks and Mailboxes as needed to deal with interactions and asynchronous programming.

The foundational pattern for user interface development is the MVC pattern. This pattern has evolved into a family of related patterns which we refer to collectively as the MV pattern. In general what these MV patterns are trying to do is to separate the data, the behavior/code and the display. We will not be able to explore all the aspects of this pattern here, instead we will look at simple examples in Elm that use some aspects of this pattern.

The elm-architecture document encodes this pattern and we have the library StartApp that we can use to write user interfaces.

Essentially we decompose the problem into the following components:

  • Model
  • View
  • Action
  • Update

So let us start with a simple example:

Model

The first idea is to represent all the data for the application in a data structure that we call the model. Here is a simple example of a model that contains a string.

type alias Model = { message: String}

As you can see the model is just an Elm record. So it can be extended as needed. Spending time to design the model for your application will be tremendously beneficial.

View

The view in Elm is written using the Elm html packages:

  • Elm.Html
  • Elm.Html.Attributes
  • Elm.Html.Events

As you will see below the HTML DSL in Elm is very similar to the standard HTML structure, so translation of HTML into ELM html is fairly straight forward.

Here is a simple example of a view with two HTML buttons and a HTML text field.

-- VIEW
view : Signal.Address Action -> Model -> Html
view address model =
  Html.div
    []
    [ Html.button
        [ Events.onClick address (ChangeLabel "Welcome To Elm") ]
        [ Html.text "Click Me" ]
      ,Html.button
        [ Events.onClick address (ChangeLabel (String.reverse "Elm Welcomes you")) ]
        [ Html.text "Click Me 2" ]
      ,Html.div [Html.Attributes.style[("font-style", "bold")
      ,("font-size", "20px")
      ,("color", "blue")
      ]] [ Html.text model.message]
    ]

Actions

Since there are different actions that represent user interactions in our application, we use a ADT to represent the actions. The type constructors for the Actions may or may not take a parameter as required.

Here is the Action type for this application, it contains one non-trivial action type ChangeLabel that takes a string argument for its type constructor.

type Action = NoOp | ChangeLabel String

Update

The last element that we need is to encode the logic to update the model based on the Action type. This is done by pattern matching on the Action type and invoking the appropriate code based on the Action.

update : Action -> Model -> Model
update action model =
  case action of
    NoOp ->
      model

    ChangeLabel val ->
      {model | message = val}

Start App

Putting this all together we have our first Web Application.

import Html exposing (..)
import Html.Attributes exposing (style)
import Html.Events exposing (onClick)
import StartApp.Simple exposing (start)
import Html.Events as Events
import String

type Action = NoOp | ChangeLabel String

type alias Model = { message: String}

update : Action -> Model -> Model
update action model =
  case action of
    NoOp ->
      model

    ChangeLabel val ->
      {model | message = val}

-- VIEW
view : Signal.Address Action -> Model -> Html
view address model =
  Html.div
    []
    [ Html.button
        [ Events.onClick address (ChangeLabel "Welcome To Elm") ]
        [ Html.text "Click Me" ]

      ,Html.button
        [ Events.onClick address (ChangeLabel (String.reverse "Elm Welcomes you")) ]
        [ Html.text "Click Me 2" ]
      ,Html.div [Html.Attributes.style[("font-style", "bold")
      ,("font-size", "50px")
      ,("color", "blue")
      ]] [ Html.text model.message]
    ]

init : Model
init = { message = " " }

main =
  start
    { model = init
    , update = update
    , view = view
    }

results matching ""

    No results matching ""