#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.

Basic debugging in Elm

This blog purely focus on very basic debugging. No time travel or anything fancy. Simple debugging features.

We will start with our baisc code which takes first name and last name in two different input field and when user pressed ‘FullName’ button, it displays the firstname + lastname in Full Name: label.

image

The program I started out is here in the Gist. With that code, when you run the application and enter the first name and last name like the following

image
and press FullName, you get the following result

 image

The last name is missing and the first name is added twice. (It is easy to fix but for the sake of debugging, we will continue as if we do not know the reasons).  So how do we go about debugging in the Elm?

For starters, when you run the elm in elm-reactor mode, the application runs in debug mode. When the application running in the browers, you will see small box at the bottom right hand side of the screen something like the following

image

If you click to open the window you will see two sections

One on the left, shows all the events that triggered. The one on the right side shows the current state of the model.

image

This will be great means to see all the events that get fired and also for each event how the model changes.

The second approach is the age old logging. Elm provides a mean to log messages to the browser console. We will start with the first problem, that is, the result of first name and last name shows first name twice.

We have following code for Add message

Add ->
           { model
               | fullName = model.firstName ++ model.firstName
               , firstName = “”
               , lastName = “”
           }

even though it is very clear that, we are adding firstName field twice, for the sake of showing logging, we will add logs. To add logs in Elm, you need to import Debug package, which is already part of the Core package you have installed.

1. Import Debug

2. Add log messages to see what is the value of the two fields we are adding

Adding log is pretty simple and very original. Before adding the log the Add event function was

Add ->

{ model

| fullName = model.firstName ++ model.firstName

, firstName = “”

, lastName = “”

}

To log you do not need to create a seperate line statement. You can do it inline like the following

Add ->
     { model
         | fullName = (log “first name : ” model.firstName) ++ (log “last name : ” model.firstName)
         , firstName = “”
         , lastName = “”
     }

Log function take two parameters, first one is string message and second parameter can be anything. Here is an interesting thing, in elm when you have log statement like this, log function logs the message but the underlying function (the original) executes normally. So in reality, the before and after program statement is same.

Open the developer console and execute the code and you will see the first and last name both are first name. So looking at the code we know the insted of adding the last name we were adding the first name.

image

Change the first name field to last name and run the code with the log and now we get different message. Instead of repeating the first name twice, now it displays only the first name and not the last name.

image

You can find the code with logs here in this Gist. Now let’s look why are we not getting the last name. We are going to use, time trave/history tab to see what is happening.  When a user enters a value in the input, for every keystroke onInput event fired. So let’s start there. We need to check and see if appropriate action event fired.

Based on the update event,

type Msg
     = Add
     | Clear
     | InputFN String
     | InputLN String

We expect InputLN event fired. Let’s open the tab and monitor the events.

First I typed ‘unni’ and you can see blow, InputFN fired for each keystroke and when I clicked FullName, you can see the Add event fired as well.

image

With this much information first thing we need to do is to check, do we have the view wired up properly to fire the events on data entry.

, input [ onInput InputFN ] []
, input [] []

The error is obvious, for the second input box, we do not have an onInput event.

Adding the onInput and running the code gives the expected answer.

image

This is the first stab at the debugging in Elm. You can find the final version of code here in this Gist.

Working with Input Fields in Elm

Continuing on the ‘SayMyName/Say Hello’ example, we will examine the input tag to understand the events and state management. Like other examples, let’s look at the final output first

image
We have added a new button ‘Clear’. When someone clicks clear button, it will clears the value in the input tag. 

Like other examples, we will start from the Model. There is no difference between the previous example model and this one.

type alias Model =
     { name : String
     , inputValue : String
     }

initData : Model
initData =
     { name = “”
     , inputValue = “”
     }

Next Update. Like we discussed in the previous blog, update has two parts, one to define a message associated with an action and second, execution or acting on action.

Defining the action:

type Msg
     = SayMyName
     | InputData String
     | Clear

We added the last message ‘Clear’ to our valid actions. Next we need to act on it. If and when someone presses the clear button, what we need is to clear the value of the input box. ‘inputValue’ holds the current value of the input box, so in our action execution, all we have to do is to set it to empty string. Something like the following

update : Msg -> Model -> Model
update msg model =
     case msg of
         SayMyName ->
             { model | name = model.inputValue, inputValue = “” }

        InputData val ->
             { model | inputValue = val }

        Clear ->
             { model | inputValue = “” }

The last two lines were added to clear the input value.

Finally view, in view we have to do two things;

  1. Define new Clear button and onClick we send out ‘Clear’ event. That is achieved through following definition in view

, button [ onClick Clear ] [ text “Clear” ]

2. When a user clicks the clear button our update function clears the value of inputValue in the model. To reflect the current value of the model, we have to set the value of the input box to model’s inputValue like the following

, input [ onInput InputData, value model.inputValue ] []

You can find full code here at this Gist.

Continuing the read code – Finally the view

We are building a very simple application which does not do anything fancy so that we can focus on the fundamentals of Elm. In the past two blogs we looked at

  • Model
    • We talked about type alias to keep the code readable by defining the alias for the core data structure so that you do not need to define it every time when one need to define the function definition
    • We also need to define the initial state of the application as part of the model definition. The main reason we need this to make sure on page load, the view is rendered through the initial state of the application
  • Update
    • Compared to other programming model, we need to define all the actions that are going to happen in a given application by defining the message types by using Elm specific type (union type or traditional enumeration type)
    • We need to create a function which will then act on a given action by changing the current state to new state, which will then trigger view rendering by taking the current state of the application and build the modified DOM elements

Basically Model defined the initial state and data structure while Update create the current state of the application. With that what is left is to render the view.

Traditionally most of the frameworks uses HTML or a template to build the view. In Elm, a true functional programming language even rendering HTML view done through functional model. In Elm, every html tag is a function. Each of these functions takes two list of arguments. First one is all attributes and second is list of all its children.

Let’s look at the code which will render our HTML page and then we can deconstruct it

view: Model –> Html Msg

view model =

    div []

    [

        h3 [ ] [ text “Your name is: “ ++ model.name]

        , input  [ onInput Change

                      , value model.inputdata ] []

       , button [ onClick SayMyName ]

        [ text “Say My Name”]

    ]

Let’s go through this code.

view: Model –> Html Msg is the function definition of the view function. View function takes model as input and generate Html and Html which could create messages

The first function call in the view is div function. As stated before, first array of list represent the attributes and second array is the collection of its children.

image

As specified in the diagram above, the first array is empty, but the second array, has three children

image

So far we have defined the model, update and view. As mentioned in the theory part of the Elm, we need an entry point and plumping needed to connect all these three components. That entry point is main as defined below

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

For getting started, we will leverage a function called beginnerProgram function and pass in the initial state of the model, the view and update function. The beginnerProgram will trigger the following on load

  1. First initial state of the mode created by running initData
  2. View function will take initData and render the HTML DOM elements
  3. As the user enters data in the input field, Change event will be triggered.
  4. Update function is called every time appropriate events are triggered
  5. Update function will be called with Change function and it will pass in the current value of the input text
  6. Update function execute Change event, which does nothing put populate inputdata with the input box value
  7. When the user clicks SayMyName button, view will trigger SayMyName event
  8. Update function called again with SayMyName event
  9. Update function executes SayMyName code block, which creates new model with name being populated with inputdata and inputdata set to blank, which represents the current state of the application
  10. When ever model changes, view function get called with current state of the application and repeat step (2) and forward again

You can find the full code here at the Gist.