As your Elm project gets bigger, you start having multiple pages, all with their model/update/view. Setting up your main update function to pass the Msg
to the correct page and then convert the response back to a (Model,Cmd Msg)
is quite tricky and from personal experience I can tell you: if you do it wrong, its very hard to read and even to refactor. Today I present to you to an out-of-the-box solution that even prevents you from writing wrong or dead code.
https://package.elm-lang.org/packages/Orasund/elm-action/latest/
Example
Here is a quick example of it in action:
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case (msg,model) of
(GuestSpecific guestMsg,Guest) ->
updateGuest guestMsg
|> Action.config
|> Action.withTransition initUser User never
|> Action.withUpdate (always Guest) never
|> Action.apply
(UserSpecific userMsg,User userModel) ->
updateUser userMsg
|> Action.config
|> Action.withUpdate User UserSpecific
|> Action.withExit (Guest,Cmd.none)
|> Action.apply
An update can return one of three actions: transitioning
, updating
or exiting
. In the example we see that Guest
can update (if the logging failed) or transition into User
(if the logging was successful). A User
can update or exit back to a Guest
(by logging out).
The allowed actions are specified in the type. This way the compiler will call you out if you use a wrong action. The same happens if your config pipeline is wrong.
Under the hood, this is actually just a state machine.
Alternative Solutions
I’ve seen multiple different approaches for the same problem and it might be that my solution isn’t suitable for your needs.
- In the Elm Spa Example, a helper function
updateWith
is used for the wiring. Transitions are all defined in the functionchangeRouteTo
. Personally I like to model my app as a state machine. But if you don’t, then the approach in the SPA example will be better for you. - the-sett/elm-state-machines has the same idea as I do, but implements it using phantom types. Instead of phantom types, I use the config pipeline to specify what actions are allowed.
- turboMaCk/glue introduces the concept of subModules. This makes a lot of sense for reusable views. If you only use reusable views, then use that package instead.