#elmlang – Navigating the pages – part 1 Model

So far we were able to create a simple elm application which is able to display hard coded list of best opening credits. As you can imagine, when building non trivial application, we will end up with multiple views and based on user requests we may need to swap body with proper views. In olden days, every page was recreated and redrawn and in new SPA world only the portion of the DOM elements are swapped out with proper views.

In SPA, you can think of a page with header, body and footer. Every time when a user clicks some options in header the body will be swapped out with appropriate views.

In Elm, we solve this with navigation and routing. Everything I learned on navigation and routing came from the following three awesome links.

Instead of adding navigation to existing project, we will tackle navigation without any noise. So for our project, we are planning to have following user selections

  • All time best
  • Personal best
  • Trending now
  • About
  • Login

As the user select anyone of the options above we will show appropriate view.

To make this exercise simple and easy to follow, we will focus on just two of the five actions above,

  • All time best
  • Login

To get this one going, we need to add another elm-package called ‘Navigation’

elm package install elm-lang/navigation

Next, instead of using beginnerProgram as the lynch pin to connect model, view and update from now on we will use Navigation.program.

So why are we using Navigation.program instead of beginnerProgram? As the name suggests, this package wraps lot of navigation related activities in it and make our development easy. For starters

  1. Navigation.program is wrapped on top of Html.beginnerprogram (html.program) so all the things we learned to love is still there and more.
  2. Navigation notifies us every time there is a url change
  3. It also allows us to change the navigation
  4. It provides us with rich set of information to know what changed during url change

Like all our previous applications, we will start with model. For time being ignore our application aspect of it but focus on the navigation aspect. While we are navigating between pages, what is the one thing that we care about? We need to know which page we are in. So we need to capture that as a field in our model. Good start, we know our model has one field and it holds the current page name possibly. So what type it should be, traditional thinking would make us to define it to be String. But remember we are in Elm and we want to be as much explicit as possible. In that case, instead of defining it as a string, we could define it to be a union type of all known pages. So, let’s start by defining all the known pages

type Page =

    AllTimeBest

  | Login

So we defined a union type that defines all the possible pages we are going to support. With that, our model should be something like the following

type alias Model =

   { page : Page

   }

So we defined the model and obvious next step is to define initial state of the application.

initstate: Model

initstate =

   { page = AllTimeBest

  }

So far nothing new. That is all for model part, next we will see update part.

You can find the full source code the example here are https://github.com/ksunair/bestopeningcredits/blob/master/simplenav.elm

Advertisements

#Elmlang – Displaying a list

Continuing our project, in the last blog we took our initial step at displaying a single best opening credit with a simple model. In this blog we will see how do we go about displaying list of opening credits.

Let’s start with model. In the previous example, what we had was just one record with title and url. Now to show a collection, we need to create list of records. Let’s see how would we define the list of records

type alias Model = List

   {  title: String

    , url: String

   }

All we did in this definition compared to previous one was to add ‘List’ in the definition. Even though this definition is all and good, there will be times where you will need to pass just a single record between functions, like displaying one record. So we need to split the definition to represent a record and list of record. With that refactoring the definition would look like this

type alias Model = List

     Record

type alias Record =

      {  title: String

        , url : String

       }

Here we defined a single record definition as record and then created a Model which is list of records.

Next we modify the initial data to hold more than one best opening credits. So I added True Detective season 2 opening credits.

initData : Model
initData =
     [ { title = “Game of Thrones”
       , url = https://www.youtube.com/embed/s7L2PVdrb_8
       }
     , { title = “True Detective Season 2”
       , url = https://www.youtube.com/embed/GJJfe1k9CeE
       }
     ]

There will be no change to update function as there are no user interactions yet.

View is going to be interesting. So far all the controls we have created are all just single controls never displayed anything that is a collection.  How do we go about doing this?

Let’s look at the view from previous blog

view : Model -> Html Msg
view model =
     div []
         [ h1 [] [ text initData.title ]
         , iframe [ width 420, height 315, src initData.url ] []

        ]

The above view function takes Model and creates the view for Elm to render. Now that we modified the Model to List, we need to modify the definition to be Record instead of Model to just display single record.

view : Record-> Html Msg
view record=
     div []
         [ h1 [] [ text record.title ]
         , iframe [ width 420, height 315, src record.url ] []

        ]

This can not be the view, because view needs to generate all the records to display, so let’s change the name of view to a different name to represent what it does, it displaying a single record

displayLine : Record -> Html Msg
displayLine record =
     div []
         [ h1 [] [ text record.title ]
         , iframe [ width 420, height 315, src record.url ] []
         ]

Now we need to modify the view to call this function for every record in the list, that is it. In Elm, there is no iteration or loop instead we will call map function.

view : Model -> Html Msg
view model =
     div []
         (List.map displayLine model)

Let’s look at the view definition closer. View definition states, it takes Model (which is list of records) and generate the HTML view for Elm to render. The view function definition has just one function call that is div. As stated in the previous blog, view function takes two arguments. First set of arguments are the function attributes and second set of arguments are list of children.  In the above definition, the second parameter is (List.map displayLine model), what does it mean?

Let’s look at the definition from left to right. There are three words in the statement. ‘List.map’, ‘displayLine’ and ‘model’.  We know the last two, ‘displayLine’ is a function which takes a record and display the records title and url. The last parameter is ‘model’, which is list of record. So it looks like we are calling List.map function, with two parameters. 

List.map’s first argument is a function which takes list as a parameter and produces list as result.

With that if you look at our call, List map call displayLine for every record in the model and generates one line display for the given record. List.map function always returns a list. Like all HTML function calls, Div function expects the second argument to be list of children and List.map returns the list of displayLine.

If you run the application, you should see a result similar to the following

image

You can find the latest  code @ https://github.com/ksunair/bestopeningcredits

Let’s work on displaying single best opening credit – Elm

Continuing our project to display the best opening credits, let’s jump in start displaying hardcoded single best opening credit.

Before we start coding, let’s refresh quickly on the anatomy of Elm architecture. In Elm application, you have three separate parts each doing its one task and one task only. The three components are

  • Model: This defines the data store and it also defines the initial state of an application.
  • Update: This section defines what are all the actions/events application supports and what do with the state of the application.
  • View: This defines the view of the application based on the state of the application.

Elm architecture is a very simple one. In Elm, all the application state is maintained separately in the model.  On application start, Elm runtime will render the view using the initial state of the model. In the view we define what are all the events a user will be able to initiate. When a user initiate an action or trigger an event, Elm run time will call update function and pass in the incoming event/message and current state of the model.Update will act on the current state based on the event and create a new state, the new state of the application will then trigger view to render new view with updated state of the data.

Now with basics under our belt, let’s write a code which displays single best opening credits.

Like all the application, let’s create model first. There is always two question we need to ask when defining model (there are more but for creating trivial application, two is enough)

1. What is definition of the model? What fields do we need? For this simple application all we need is ‘title’ and link youtube video of the opening credit

type alias Model =
     { title : String
     , url : String
     }

2. Now we know what is data definition, let’s create the initial data that we want to display.

initData : Model

initData =
     { title = “Game of Thrones”
     , url = https://www.youtube.com/embed/s7L2PVdrb_8
     }

There are two things I want to call out, first the idea of using comma in the beginning of the line instead of the end. When we look at it seems a good coding practice but in Elm it is more than that. Since Elm is functional programming language if you miss a comma, the way a function being called will be different.

Second thing is the type definition, even though Elm can infer the types, I would strongly recommend being explicit in your code and declare the types. It helps for future code refactoring and maintenance.

Next Update. Since this is a trivial application with no user interaction, we will define a dummy update function. Again we will continue to ask the same two questions.

  1. What are the events are we expecting?

type Msg
     = DoNothing

This is a dummy event to satisfy the beginnerProgram function,

2. What to do when a update function is called?

update : Msg -> Model -> Model
update msg model =
     initData

Since there are no events to start with we are not acting on any events.

Finally the view. In the view, we are displaying just one record. The title will be bold and then link to yourtube video.

view : Model -> Html Msg
view model =
     div []
         [ h1 [] [ text initData.title ]
         , iframe [ width 420, height 315, src initData.url ] []

        ]

Our view has main ‘div’ with two children, one is h1 and another one is iframe.

h1 does not have any attributes but has a child text which displays the title.

iframe has attributes of heigh, width and the url source.

That is it,

the final component which connects all together is main

main =
     beginnerProgram { model = initData, view = view, update = update }

run the elm reactor which will start the localhost:80 and selecting Main.elm will produce the following result

image

Starting Opening Credit Project in Elm & Horizon

Finally I found some time to start the project. I am doing this project in a way to learn Elm.  To do full end to end development with focus on front end development, I wanted some light weight backend service. Initially I was thinking of doing it with nodejs but I came to find out there is a better one for especially for these purposes like rapid development. Horizon is meant for that. I do not know much about it yet. I am planning to use this project to learn both of them.

Created the base line for the project at https://github.com/ksunair/bestopeningcredits 

For this project, I am using Atom editor.  I am using following packages

git-plus – to better integrate with Git

elm-format

language-elm – Syntax highlighting and auto completion

linter-elm-make – Lint Elm

The main objectives of this projects are

  1. User should be able to login to the site using social media logins
  2. Each user should be able to nominate any of their favorite opening credits
  3. Every user gets to vote for their top 10
  4. Show all time top 10 based on all user votes
  5. Show last week, month top 10

With this project, I intent to look at the following

  • Creating modules to separate the concerns
  • Create unit tests
  • Make http calls
  • Manage routing and navigation

The base project is setup and next blog we will see how we can create a simple page to allow user to nominate the opening credits.

Elm is pure functional programming language

While preparing for my presentation on ‘Introduction to Elm’, I was trying to prove Elm is a true functional program by showing this below

add: Int –> Int –> Int

add x y =

   x + y

In the above example, add function is pure, for a given input add function always create the same output. So calling Add 2 3 will always return 5. It does not have anything of global scope or side effects. So if I were to introduce a global variable Elm will through a compiler error. I was expecting the following code to produce a compiler error

num: Int

num = 10

add: Int –> Int –> Int

add x y =

    num + x + y

Elm did not complain and it worked. So if I were to pass add 2 3 and the result will be15. So in my mind because of the function depends on outside variable num and someone can change num from outside  the add function is not pure and thus Elm is not functional programming language.

So I posted the question in Elm Slack Channel and @mfeineis answered “Elm is pure and num is immutable”, then it hit me. I assumed few things as a normal imperative programmer would. The num is a variable and it can be changed easily but in functional programming num is not a variable and there is no concept of variable in Elm. So you can’t change the value of num. With that knowledge if you were to look at the function, the function name  is incorrect, it should reflect the proper intention of the function and it should be ‘addnumwithxandy’.

@jessta reply summed up the answer “ `num` and `10` are interchangeable because Elm is ‘referentially transparent’. There’s no difference between a value and a variable containing that value.”

For the people I owe an explanation, hopefully this blog helps and for those helped me in slack channel, big thanks.

Working with backend services in Elm

Before we go past building trivial solution, we need to understand one more concept in trivial example scenario. How do we go about making backend calls? More preciously how to connect and work with API calls.

We will start out with looking at the final result and then we will go about building them. We are building a very simple application which will show the current market value of bitcoin. When you start the application you will see the following screen

image

The screen shows the the initial data of 0. On pressing ‘Get Data’ button, the application will make an API call to “https://api.cryptonator.com/api/ticker/ltc-usd”  url, and display the result as it is in the screen.

image

It does not show the value instead it just displays the whole JSON payload as it is. For our API call discussion we do not need to worry about how to parse JSON and other things, which we will discuss at a later time.

(It will be interesting to come back after couple of years and check the value of bitcoin again 🙂 )

Let’s start building the application without api call first to get the basic screen shown.

First Model:

type alias Model =
     { rate : String
     }

initData : Model
initData =
     { rate = “0”
     }

We have just one field in the model record, which we kept is as String for the sake of simplicity.

To get started we create the update section with

type Msg
     = GetData

update : Msg -> Model -> Model
update msg model =
     case msg of
         GetData ->
             dummyData

As you can expect, when a user clicks ‘GetData’, the GetData message fired by html. That is the only event we needed for the application right now. The action event on GetData does nothing but return a dummy data. It is not making a API call yet.

View is simple and straight forward. One label to show the rates and another button which initiates the GetData message.

view : Model -> Html Msg
view model =
     div []
         [ h3 [] [ text (“Current rate = ” ++ model.rate) ]
         , button [ onClick GetData ] [ text “Get Data” ]
         ]

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

When you run the code first time, it will show the init data with 0.

image

On clicking GetData, it shows the dummy data as expected.

image

You can find the whole base code here in the Gist.

Time to add API call. There is one concept in here will look odd but it is easy to understand. Before we go to API call, I want to go one more time how html and our code interact.

view: Model –> Html Msg

update: Msg –> Model –> Model

In the above code snippet, the view takes the model and creates Html which is capable of creating Msgs (like onClick or onInput). What goes in creating the message is hidden inside the Html module and we do need to worry about, all we need to know is, when some user action happens, Msg will be triggered and ‘update’ function will be called immediately to act on it. 

Now the same way, let’s think about API call, when we make API call we need to wait for the call to complete and up on  completion, we need to modify the model and change in the model will update the view. Very simple. One difference between the button click event and the API event is that when API is called, there is a possibility the call might fail. So the result could come back as either successful (Ok resultValue) and the value or it could comeback with Error (Err _) with error message. So we need to handle both the scenarios in the code on completion of the API call.

Let’s recap what we discussed so far

  • We need to treat API call similar to Html event calls
  • We need a mechanism to fire a message on completion of API call
  • On completion we need to test for successful execution of API and error scenario

It seems there is a lot but not really. Let’s take baby steps and grasp the concept. To make API calls we need to install Http module. Once installed let’s write the code to make the API call. To make a Http Get we can use

Http.getString uri

this will make a call and gets string response from the uri. Simple. We could create a function which does it. But we have a problem, this return string and we need to wait for it complete. We need to modify this some how so that, on completion it generates a message which we can act on when we get it. How do we go about doing it?

It is solved by Http.send function. Http.send function, will return a message on completion of a http request. So we can modify the previous statement to something like the following

Http.send HttpDataComplete (Http.getString url)

Http.Send is a command, which will initiate the Http.getString and on completion it will trigger HttpDataComplete event/msg. So if we were to write a function to do this task it will be something like this

httpGetData: Cmd Msg

httpGetData which does not get any input data will generate a command which is capable of generating messages. Now let’s look at the implementation

httpGetData =

     Http.send HttpDataComplete (Http.getString “https://api.cryptonator.com/api/ticker/ltc-usd”)

We have a function to get data. Now we need to call this function when Get Data button pressed.

Our current update function signature is

update: Msg –> Model –> Model

This reads, update function takes Msg and current state of the model and generate new model. But the changes we made, when some one presses GetData button, we not only generate model but also create a command which could create msg (like out view defintion view : Model –> Html Msg). So our update definition now changes to

update: Msg –> Model –> (Model, Cmd Msg)

With that definition, when a user clicks on ‘GetData’, we need to call the httpGetData function. We will do it something like the following

case msg of

     GetData –>

            (model, httpGetData)

if you look at the return state here, it following the signature of (Model, Cmd Msg).  Now that we made the call how are we going to handle the response from get data call?

When you make a API call, you can get either a success or failure. On success, we change the rate value with result value. On failure, we need to reset it to initial data. In Elm we have something Result which helps with this situation. Anytime when we perform an operation which could result in success or failure then we need to use Result. With that, when Http make HttpDataComplete message, it will come back with result. So we need to modify the signature of our message to represent this scenario

HttpDataComplete (Result Http.Error string)

Now in our update function, when we get HttpDataComplete, we need to check for success and failure in the case

case msg of

     HttpDataComplete (Ok data) –>

     HttpDataComplete (Err _) –>

The second (Err _) parameter means, when you get an error with any value perform that function.

Now we know we need to handle both the scenario, what should be do when API calls comes back successful? All we need to do is modify the rate value in the model with incoming data. Now if you remember, our update function expect the second parameter to a command generating function. In our case after updating the model, we do not need to initiate any commands so all we need to pass is Cmd.none. On error reset the data model to initial data and same as success scenario, pass is no commands. So here is the completed update function

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
     case msg of
         GetData ->
             ( model, httpGetData )

        HttpDataComplete (Ok val) ->
             ( { model | rate = val }, Cmd.none )

        HttpDataComplete (Err _) ->
             ( initData, Cmd.none )

We are not done yet, two more things we need to do. When we move to this model of program, it is possible you might want to load data from backend API on page load that means we need to call that function without any program intervention. To accomplish that, we need to move to a different helper function called ‘program’ in Html.

Html.program takes different set of parameters

  • First parameter is initial data. It includes the initial model and any function that need to be executed to load data from back end. In this example, we are not expecting to load any data from API on load. So the values (model, Cmd.none)
  • Second is view and there are no changes to it
  • Third parameter is update function. No change there.
  • Final parameter is subscription. For Http example there are no subscription so we are going to create a dummy subscription to satisfy the signature in a minute

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

You can find the final source code for this here in this Gist.