I have been on an Elm journey these past two month. So far it has been a challenging but also enjoyable experience. Coming from a React/Redux environment, and having to let go of reusable Components and switching to reusable functions took some time. But the neurons are connecting and writing code in Elm is becoming less of a challenge and more exciting every day.

I wrote a reusable component with React in my current project to handle rendering data tables that provide pagination, sorting, filtering, and selecting records. The component is used by about 15 different data sets. Naturally I wanted to see how I could develop this in Elm

The beginnings

I started by defining a function that would take List record and return Html msg where both record and msg refer to a generic type

-- Reusable function that will print the length of a list of records table : List a -> Html msg

table records =

Html.div [] [ records |> List.length |> toString |> Html.text ]

But how would the table know — how to iterate the fields of the record, and render column headers, rows and cells?

In React I would pass a list of objects describing each record’s field.

It’s name,

how to receive it’s value,

and optionally how to render its value.

The component would then iterate the array of records — and for each record iterate the array of objects describing how to render a specific field as a table cell.

I made a distinction between how to retrieve a record’s field value since sorting and filtering only need raw values, but rendering the table cells could translate to anything. (Text, check-marks, images, etc.)

For this article I am going to use the following data model:

type alias Fruit =

{ id: Int

, name: String

, colour: String

, available: Bool

}

Imagine a list of Fruit models. What our reuseable table function will need is a list of view functions. It can iterate over this list to render each cell by passing the current row’s record to it.

type alias Cells record msg = List (record -> Html msg) table : Cells record msg -> List record -> Html msg

table cells records =

Html.div [ class "table" ] (List.map (tableRow cells) records) tableRow : Cells record msg -> record -> Html msg

tableRow cells record =

Html.div [ class "table-row" ] []

I’ve defined the type alias Cells record msg here to refer to a list of functions that take a record and return Html msg . Just to keep the functions type definitions readable.

table will take Cells and List record , render a div and and iterate the list of records with List.map using a partially applied function named tableRows .

Partially applied because tableRow is a function that takes Cells then record and returns Html msg

Calling tableRow cells returns a function that takes record . In Elm you don’t have to pass in all the function’s arguments at once.

List.map expects 2 arguments.

A function that takes record

List record