SPA in Elm: Part 3
Creating Reliable UI with MDL
In previous article we’ve created working Single Page Application with 3 screens. But it looked like a “Hello form 80s”, just black text on white background. Let’s fix it now. To be honest, I’m not a great designer, that’s why we’ll use Google’s Material Design Light framework here. It makes web page look like modern mobile application. I have no quick idea about how to design counters themselves, so I live them “as is” for now. You are welcome with suggestions in comments or on Reddit.
First steps with MDL
To use MDL in our project, we first need to add debois/elm-mdl package.
elm-package install debois/elm-mdl
Then we need to update index.html file and add MDL related stuff. This includes base and icon fonts for MDL and stylesheet for its components.
primary and secondary here are colours for interface. You can choose your colour combination here or just omit it, if you are ok with default one, which looks fine. So for now we’re going to use default.
Model
We need to add field for mdl internal state to our Model and initiate it. It’s nothing special, here is the code.
Update
Update is a bit more interesting and needs some more discussion. I haven’t yet found a way to handle Mdl messages without duplicating them in each <screen>.Update module. If anyone here can help with this, you are welcome. Here is my code for this.
Subscription and initialisation
MDL Layout functionality needs to add Subscription to our code to work properly. So let’s do.
Router
Router doesn’t change dramatically, but we add new field for screen header to Route record and update everything connected. For the purpose of the tutorial we assume screens to have static headers.
Screen
This is completely new module which is used for general screens layout. So we can pay more attention to functionality of each exact screen without duplicating general layout code.
Render type here is an alias for functions that takes Model and returns some Html. They are used very often in Layout so it’s convenient to have an alias.
defaultLayout function is used by most screens to show all this beautiful stuff MDL have, including header, left menu (which is called Drawer) and all the rest is screen’s body. It takes Model and a Render for screen body.
viewHeader function renders screen header for us. Header for screen is taken from new field of Router.Route record.
We don’t need tabs for this tutorial so we put empty list for their contents.
View
If we start out app now, it will compile, and work as before. Nothing changed except code. To see everything in action we need to update Views. I’m going to fix one screen, all the rest is for homework.
Firstly ,we change our root View to display MDL screen instead of our previous content.
We use our Screen.defaultLayout function here, which makes code readable and avoid duplication.
Next , we refactored First.View to render only main body of the screen without common layout.
Finaly, we’ve made button look more pleasant. To make the button MDL-style we need to change it from Html.button to Material.Button.render. In Properties list we can add a couple of functions which modifies look and feel of the button and assign messages to events. You can see some examples here. The most tricky thing here are Index argument which is some strange and not enough documented list of integers. As I’ve discovered it should be uniq for each button. Some information about it can be found on StackOverflow. I’ll try to describe it in some article later.
Since it’s just a tutorial, I have no appropriate idea about counters styling, so I leave them as is. You can write me your ideas about it as well.
Code
All code from this article can be found on GitHub. It has one branch for each part. You are welcome with to fork, use and star it as you like.
PS
Dear readers, thank you for your interest and feedback. I was very glad to see so much interest about my Intro, Part 1 and Part 2. If you like this one you are welcome to clap, subscribe and connect me. I’m waiting for new feedback!
You can contact me on Twitter (@t0ha666), Reddit (/u/t0ha), Telegram (t.me/war1and) or Email (t0hashvein@gmail.com).