Getting Started with Elmish
I’ve been learning F# lately and have been wondering what the F# in the browser experience is like.
Lets install Fable and then walk through the basic Elmish counter app.
Fable has a very good Getting Started section. The following dependencies are required on your local machine:
- .NET Core SDK to work with F# files and dependencies
- Node.js to execute JS code
- A JS package manager, like npm (which comes with Node) or yarn
After getting those installed and having a IDE that supports F#, I’m using VS Code with the Ionide plugin, download the fable2-samples project to try out your first Elmish app.
Minimal Elmish Example
After download, cd into the /minimal folder, run
npm install, then
npm start, and point your browser to
http://localhost:8080/. You should see the counter page loaded.
You can play around with it, adding and subtracting to your heart’s content.
The counter is built using Elmish, which is a UI library based on Elm, hence the -ish name. Elm is a very opinionated UI library based on the Model-View-Update (MVU) architecture.
In my experience, UI development is a pretty wild and typeless place, and I’m not a huge fan of that. The opinionated nature of Elm and Elmish provide a different set of abstractions and guarantees for building UIs. I’ve found Elmish a refreshing UI development experience.
The project structure is very simple, the two important files are the index.html and the App.fs. The index.html is very simple but the App.fs deserves a closer examination.
If you’ve never really seen an MVU app before, perhaps this is a bit strange to you.
Via the comments, its easy to see the main parts of the MVU app. Lets dive into the MVU architecture and talk about how works and produces a UI.
Even though it’s called the MVU architecture, lets start out by talking about messages.
Messages are how things change in an Elmish app. When a user clicks a button, the Elmish runtime dispatches a message. When a call to a REST Api returns, the runtime dispatches a message.
Every action taken in our app needs to have a message representing it. Our simple app has two message types, Increment and Decrement. So the only possible things our app can do is Increment and Decrement. If we wanted any further functionality we would have to define different messages.
Once a message is published, the Update method receives the message and the model then determine how our app should change.
In our app, for an Increment message it adds one to the model, and for a Decrement message it subtracts one.
Our model holds the state of our application. Anything worth tracking is stored in the model. In bigger apps, this can get complex.
In our simple counter app, the state is just an int, the current number of the tracker.
Then finally the view. In an Elmish app, the view method is what is displayed to the user. The UI is declared in an HTML like syntax, notice the div and button elements.
After the update method runs, the resulting model is passed to the view method to render the new UI.
The fact that the view is written in F# instead of just html strings means you can get compile time checking on your html. Say you misspelled the button element, F# would produce the following message.
Compile time guarantees for UI work is something I’m not used to. It’s great!
If you’ve never seen this syntax for writing html, here is how to define a header with a particular class name.
Anything that would normally go inside the open tag of the html element is placed in the first , then anything normally in between the open and close tag of the html element is placed in the second .
You can nest them together to create any type of HTML layout. Here are two more examples.
Adding a New Feature
To get a more complete view of how Elmish works, lets add a Reset button that will return the counter back to zero any time that it’s clicked.
The first thing we’ll have to do is change our message type. We need to add a new message type, Reset for our new feature.
With that change, you’ll see that the compiler throws an error. It complains on our update method saying “Incomplete pattern matches on this expression. For example, the value “Reset” may indicate a case not covered by the pattern(s). Another compile time check to help you write better UI code and avoid issues!
This is F# telling us that our update method no longer has actions for every message. It’s missing what to do if it receives a Reset message.
Lets modify our update function to include the Reset case, and output a zero to indicate the value is cleared.
Lastly lets add a button to trigger the Reset button in our view model.
Navigating to our app now we see the updated UI. (Pardon the bad styling)
Anytime you click the Reset button, the value of the counter will be returned to 0!
There you have it! You’ve gotten your feet wet in the MVU architecture. If this kid of UI development intrigues you, If this kind of UI development is new to you, I’d recommend reading further about Elmish or Elm. From what I can tell Elm has better documentation for getting started than Elmish does.
Complete Updated Elmish Counter App - Github Gist