SPA simple with Elm Navigation

Pablo Fernández Franco
3 min readJan 3, 2017

--

Few days ago I’ve developed a small personal application. I needed to have a website with static information to help users.

Thank to Elm’s Navigation component we can do a static web easily.

Packages required

  • elm-lang/core
  • elm-lang/html
  • elm-lang/dom
  • elm-lang/virtual-dom
  • elm-lang/navigation

A static website does not need evancz/url-parser package.

The elm-package.json may look like this:

The program function

The Navigation’s program function is a bit different from the Html package.

The program function that we know looks like this:

program : { init : (model, Cmd msg)
, update : msg -> model -> (model, Cmd msg)
, subscriptions : model -> Sub msg
, view : model -> Html msg
} -> Program Never model msg

However the program function from Navigation package is defined as:

program : (Location -> msg) ->
{ init : Location -> (model, Cmd msg)
, update : msg -> model -> (model, Cmd msg)
, subscriptions: model -> Sub msg
, view : model -> Html msg
} -> Program Never model msg

The first argument is (Location -> msg). This converts a Location into a message whenever the URL changes. That message is fed into our update function just like any other one.

On the other hand, init function takes Location as first argument. This lets you use the URL on the initialization.

The model

In the application’s state we are going to save what is the current page. Therefore let’s define a ADT (union type in Elm) for the pages:

type Page = Home | About | Contact

Home, About and Contact are functions that return a Page type. The type annotation for the Home function (per example) is:

Home : Page

I have defined the model just like this:

type alias Model =
{ page : Page
}

If you want more information about ADT and union type, you can follow this link.

The update

Our update function gets messages whenever the URL changes, through the function specified in Navigation.program function.

We will define that function in the union type Msg:

type Msg = UrlChange Navigation.Location

The type annotation from UrlChange is:

UrlChange : Location -> Msg

You must remember that Location belongs to the Navigation package.

Location is a type alias defined as:

type alias Location = 
{ href : String
, host : String
, hostname : String
, protocol : String
, origin : String
, port_ : String
, pathname : String
, search : String
, hash : String
, username : String
, password : String
}

In this example we are only going to use t{he hash value. If you need to do a more complex website, you will need the evancz/url-parser to turn a Location into something more useful in your update function.

In this example the update function looks like this:

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
UrlChange location ->
{ model | page = (getPage location.hash) } ! [ Cmd.none ]

getPage is a function that receives a a hash and returns a Page function:

getPage : String -> Page
getPage hash =
case hash of
"#home" ->
Home
"#about" ->
About
"#contact" ->
Contact
_ ->
Home

The ‘_’ symbol is for to have branches for all possibilities, thus “#home”, “#about” and “#contact” have a Page associated and everything else (including empty string) has the default Home page.

Therefore defining the views for each page is very easy.

The view

In our model we have a page field that tells us which page we are on, therefore we can define the view function just like this:

content : Model -> Html Msg
content model =
case model.page of
Home ->
h1 [] [ text "Home page!" ]
About ->
h1 [] [ text "About page!" ]
Contact ->
h1 [] [ text "Contact page!" ]

In each branch we will define the page content.

Final ideas

Elm is magic and gives you superpowers. You can easily write your application and if it compiles then it works. No runtime exception. No messages like “undefined is not a function”.

In this article I have shown how to easily make a static webpage. This example can be made as complex as you want.

You can find all code in this Github repository.

If you liked this, click the 💚 below so other people will see this here on Medium.

--

--