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.
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
.
-- MODELtype 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 ofelm-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:
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.