This is part 2 of the series IHP with Elm
When initializing Elm, you can set initial values through something called flags.
For this part of the series, I want to show you a technique for loading these values called flags directly from IHP into Elm in a type-safe way.
And what's even cooler, we will also enable you to generate Haskell types to Elm without writing any decoders or encoders manually 😍
Starting out simple
IHP is a full-stack web framework, and Elm should as mentioned in the previous post not be used for absolutely everything view-related in an IHP app.
In IHP, use Elm only when you start to think "I actually need Elm". That will keep the complexity down and let you use Elm for what it's great for.
All this being said, the examples in this tutorial are made extremely simple to make the process easier to follow.
Create an IHP database type
To demonstrate how we can insert different datatypes into Elm, let's create a relatively complex database table.
Run the app with
./start and go to localhost:8001/Tables.
Code Edit toggle in the top left corner and paste this snippet into the code area:
Remember to press
Save down in the bottom. It's a bit hidden, so easy to miss.
After saving, press
Update DB in the IHP dashboard. This should update the database with the new table.
This will have automatically created the type
Book. Let's create a controller for it next.
Generate Controller and Views
Stay in the
localhost:8001 admin dashboard and select
Codegen in the menu to the left.
Controller, name it
Books and click
Preview and click
You will now have generated all you need to view, update, create and delete books. Pretty cool!
Just a couple of small adjustments before we proceeed:
Let's just use a checkbox field for the
hasRead value and a datepicker for the
Edit.hs in the
/Web/View/Books/ folder, replace these two fields:
Let's also take a short visit to the Books Controller
/Web/Controller/Books.hs and the buildBook function at the bottom. Make sure the nullable value
review turns into
Nothing if empty instead of
Then go to http://localhost:8000/Books to create just a couple of Books with some varying values.
Some small changes in the hsx templates
We are adding the Elm widget application globally because we are going to use it as a general purpose widget engine.
Web/View/Layout.hs and add the elm script
<script src="/elm/index.js"></script> to the scripts in development and remove the unused scripts for development as well.
Next, let's replace the HTML contents of
/Web/View/Static/Welcome.hs, just to have a practical link to the Books.
haskell-to-elm will let us generate Elm types from Haskell types, including encoders and decoders.
In order to generate Elm types from IHP, add the
haskell-to-elm package to haskellDeps in
To update your local environment, close the server (ctrl+c) and run
If you are on vscode, you might need to reload your text editor to catch the updates in .envrc.
Also add the required Elm packages required by
haskell-to-elm. I i highly recommend the cli-tool elm-json to install elm packages.
Reduce boilerplate for Haskell-to-Elm types
Following these instructions will make it easier to add
haskell-to-elm types later on.
Create a folder named
Application/Lib and create a new Haskell module:
Paste this code into
You probably won't ever do any changes in this script, but it saves us from lots of boilerplate when creating Haskell to Elm types.
Turn IHP types into JSON serializable types
Create the file where the elm-compatible types will live.
Web/JsonTypes.hs we will create types that can be directly serialized into both JSON and Elm decoders. For starters, we will make a
BookJSON type and a function for creating it from the IHP generated
This is some extra work, but you also get to control what fields that will be exposed to the outside world here.
Make a widget entry-point
A logical place to write the entrypoints for this Elm widget is
Application/Helper/View.hs as functions exposed here are accessible in all view modules.
We will also define a
Widget type that will be like a register for all new widgets.
bookWidget takes in the IHP
Book type as an argument, converts to the
BookJSON type and wraps it inside a
Now we need to jump to the
elm/index.js file and pass in the
data-flags attribute from the widget. We make
getFlags utility that takes in all
data-flags-* attributes and inserts them into Elm as flags.
The value passed into the
Let's put this
Now it's time for the fun stuff. We need to go back to localhost:8001 and generate a script and select
Codegen in the left menu and then
Preview and then
IHP will have created an boilerplate for an executable for you.
Fill in the export logic for generating Elm types in
Let's test it. Run:
Voila! If everything has gone well so far, you should have a file named
elm/Api/Generated.elm. Inspect it with great joy. You didn't need to write any of this manually in Elm.
What a beauty!
Let's make a
npm run gen-types script for it in
package.json and we might as well run it at the
run-dev-elm command to make sure we update it frequently.
Write some Elm
Let's finish up this tutorial by rewriting the
Main.elm to decode the flags and use the Haskell model.
Go to localhost:8000/Books and press
Show on any book you have created. You should see where Elm starts and begins with the
The Elm logic is handling every type as it was defined in Haskell, from
Bool to even
To get a complete overview of the changes, see the diff compared what we did in the previous post
We have created only one widget, but in the next post we are adding another one.
We are also structuring the widgets into separate modules, inpired by Richard Feldman's RealWorld SPA archtecture, but a simpler version.