architecturenavigationelmelm-architecture

elm state transition Msg


I've been struggling with how to organize my code in Elm, and after some googling I found this. It's not that recent but I've tried it and the result is pretty nice, compared to what I used to have. But trying to apply this to my states I find myself wanting to create empty Cmd just to send an Msg to the "root" update function, to transition from one State to the other. That doesn't seem right, I assume I'm just doing it wrong.

So let's say I have this:

type alias Model =
  { state : State
  }

type State = StateProjectList ProjectList.Types.Model
           | StateProjectView ProjectView.Types.Model

type Msg = ProjectList ProjectList.Types.Msg
         | ProjectView ProjectView.Types.Msg
         | TransitionProjectView Project

When I'm in StateProjectList, I need to be able to transition to a StateProjectView somehow but it's view function returns a ProjectList.Types.Msg and not a regular Msg. One idea I've come up with would be to create an empty Cmd that would call back using TransitionProjectView, which doesn't seem right.

The other idea would be to use a Msg like ProjectList TransitionToProjectView and match that first in the root update function, not sure about that long term though.

I did see the note on Cmd.map's doc saying if you need this you're probably doing something wrong, unfortunately the link explaining what to do instead is dead. What would be the proper way to either transition from one state to the other, or a better architecture if this whole thing is wrong?


Solution

  • This would typically be accomplished using routing. I haven't migrated to 0.19 yet, which changes the details of navigation a bit, but the basic idea is the same in 0.19 as it is in 0.18 I think. Either generate a URL for your route and render a link with that URL, or modify the URL programatically by using Navigation.modifyUrl from ´elm-lang/navigation´ in 0.18 or Browser.Navigation.pushUrl from elm/browser in 0.19, which both return a Cmd msg.

    You will also most likely want some kind of framework around this based on custom types in order to get type safe routing, instead of dealing directly with raw string URLs.

    Reading the guide pages on Navigation and URL Parsing is a good start. And looking at how things are done in Elm SPA example is always a good idea.

    If for some reason you do not want to use routing, matching on ProjectList TransitionToProjectView in the root update function seems like a decent approach. Alternatively instead of wrapping the msg in the parent using Html.map, you could have the parent pass the child a Child.Model -> Msg function which the child can use to wrap its messages itself, and thereby be able to use its parents message directly as well.