The Elm Adventure: Part 0 - Getting Started

As a software developer I always look for new technologies to learn, to expand my skill set outside of the stack I use on a daily basis in my work.

At the start of 2017 I set myself a few new years resolutions:

  • To learn a few new technologies,
  • Get involved in more OSS projects and the developer community and
  • Start blogging (again, third times a charm).

So, this is my attempt at kick-starting number 1 and 3 of my new years resolutions

This post is the first in a series of posts that will show you how to create a web app using a functional language, Elm, and the web app we will be building is a clone of Angular’s Tour of Heroes.

At the end of this post, this is what we will have built.

You can find the source code for this post here. So now that the introductions are done, let’s get stuck into some code!


Setting Up The Project

First we need to install Elm globally, we do this by running npm install -g elm and then we will create our project folder structure:

toh-elm/
|- src/
|-- elm-aspnet-core-tour-of-heroes-app/

Once you’ve created the folder structure, change into the project folder and initialise it with npm:

cd src/elm-aspnet-core-tour-of-heroes-app
npm init

Installing Elm Dependencies

To get started with writing our application we need to install the elm-lang/core and elm-lang/html packages, to install these run the following commands from the command-line:

elm package install elm-lang/core -y
elm package install elm-lang/html -y

This will install the core and html packages into the elm-stuff folder and add them to your elm-package.json file.

Creating The Elm App

For this first post, we will have all the code for our application in one file, let’s go ahead and create a file named Main.elm.

The first thing we need to do is declare our module and imports we need for our first basic app.

module Main exposing (..)
import Html exposing (Html, div, text, program)

Here we declare our Main module and what in the module we want to expose, here we use .. to just expose everything, and we import the Html type and the div, text and program functions that will be using in our application from the elm-lang/html package we installed earlier, you can use .. here as well to import all exposed types and functions in the html module.

Model

The Model holds the state of our application. For this first post, our Model is just going to be an alias for a String.

-- MODEL
type alias Model =
String

Init

The init function is called when our application first starts up, so we pass it an initial model and, optionally, some initial commands, in this case we don’t have any further commands to execute, so we pass through Cmd.none.

init : ( Model, Cmd Msg)
init =
( "Hello, World from Elm!", Cmd.none )

View

The view is a function that returns our UI based on the current model.

view : Model -> Html Msg
view model =
div []
[ text model ]

This will generate the following HTML:

<div>Hello, World from Elm!</div>

Messages

A Msg signals that something in the application needs updating, for example when an HTTP call has completed, the text in a text box changes or a user clicks a button.

type Msg
= NoOp

As we’re only building a simple app for now, we only specify a NoOp message that will return the current model with no further commands to execute.

Update

The update function is used to change the applications state, it basically just describes what the application should do when it receives a Msg.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
( model, Cmd.none )

Subscriptions

A Sub allows us to register that we are interested in certain events that happens in an app, such as location changes or listening for messages on a web socket. As we’re just building a very simple app, we don’t have anything to register our interest in so we just return Sub.none from the subscriptions function.

subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none

Main

The Main function is what actually runs our application, here we use the Program type we imported from elm-lang/html and we pass it our init, view, update and subscriptions functions.

main : Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}

Running The Elm App

To run our first Elm application, we can use elm-reactor that got installed at the same time as we installed Elm at the beginning of this post. To start elm-reactor test server, you simply run the below command from the command-line:

elm-reactor

This starts a server at http://localhost:8000 and you can navigate to src/elm-aspnet-core-tour-of-heroes-app/Main.elm file and see what it looks like. Try it out and you should see Hello, World from Elm! rendered to the screen.

Introducing WebPack

elm-reactor is great to quickly get a working prototype up and running, but to build a proper web application with external JavaScript and stylesheets we need something a bit more powerful that will allow us to import these and also other assets. For this purpose we will use webpack, you can read more about webpack here.

Installing Yarn

Instead of using npm I will be using yarn which is yet another package manager, a drop-in replacement for npm but is faster and more secure in the way it handles dependencies. You can read more about the advantages of yarn versus npm here.

To install yarn, follow the instructions on their install page.

Install Babel dependencies for ES6 goodness

As we will be writing our JavaScript files using ES6, we need to install a few dependencies to enable us to do this, we need to install the babel-core babel-loader, babel-preset-es2015 and babel-register packages:

yarn add --dev babel-core babel-loader babel-preset-es2015 babel-register

Install Webpack

To use webpack to develop our Elm application we need to install the webpack, webpack-dev-server, file-loader and elm-webpack-loader packages:

yarn add --dev elm-webpack-loader file-loader webpack webpack-dev-server

The elm-webpack-loader makes webpack aware of Elm dependencies and tracks them, this means that .elm files will be watched and also any other Elm modules it imports.

To enable ES6 in webpack we need to create a .babelrc file in the root of our app with the following content:

{
"presets": ["es2015"]
}

Now we can create our webpack.config.babel.js file that will configure webpack so that we can use it to build and test our app.

import path from 'path';

export default {
entry: {
app: [
'./index.js'
]
},

output: {
path: path.resolve(__dirname + '/dist'),
filename: '[name].js'
},

module: {
rules: [
{
test: /\.html$/,
exclude: [/node_modules/],
loader: 'file-loader?name=[name].[ext]'
},
{
test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
loader: 'elm-webpack-loader?verbose=true&warn=true'
}
],

noParse: /\.elm$/
},

devServer: {
inline: true,
stats: { colors: true }
}
};

With this config we define:

  • an entry point for our application,
  • the output settings for the generated bundle,
  • a pair of module rules around how to handle certain files depending on their file extension and
  • finally we configure the webpack-dev-server that we will be using in place of elm-reactor.

In our webpack config above we defined index.js as the entry point for our application bundle, this is where we import our HTML and Elm application. This is what index.js will look like:

'use strict';
import './index.html';
import Elm from './Main.elm';
let mountNode = document.getElementById('main');
var app = Elm.Main.embed(mountNode);

When we use elm-reactor it generates the HTML that hosts our Elm app, but when using webpack we need to create our own HTMl for containing the application, this is what index.html is for:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Elm ASP.NET Core Tour Of Heroes Demo</title>
</head>
<body>
<div id="main"></div>
<script src="/app.js"></script>
</body>
</html>

To make developing our app quicker, we can add a command to our package.json file that will start the webpack-dev-server which allows us to make changes in our app and instantly see the changes in our browser. So we will add a client command to the scripts section of our package.json like below:

{
...
"scripts": {
"client": "webpack-dev-server --port 3000"
}
...
}

Run the webpack dev server

Now we can run the following command:

yarn client

This will start the webpack-dev-server and serve our app on http://localhost:3000 and whenever we make a change to our application code our browser will refresh.

Now if you open http://localhost:3000 in your browser you should (hopefully, fingers crossed) see the below:

We made it, we created our first Elm application!

That concludes this first part of our exploration into building a web application using Elm. I hope that you have enjoyed learning how to get started with Elm and that you have found this post helpful in some way.

In the next part we will start building the Tour of Heroes by implementing a simple hero editor.