Search code examples
webroutesfrontendelmmessage

How to dispatch a message in elm (without an event)


I am trying to learn Elm, and have been trying to work out the best way to architect a scaleable application. Regarding routing, I want to have some simple logic rendering the various views depending on the current route, emphasis on simple logic. However, inside some of my views, I want to change the route depending on the model. For example, in my Players view, I only want to render it if I have received some data from the server. If the request is pending or failed, I would like to display an error page. I realize I could perform this logic inside the Players view, but I would prefer to dispatch a message to the model to change the route to a different view. I only know how to dispatch messages from events (onClick, etc...). Is this possible or am I working outside the designs of the language?

This is my current setup, but I would like to change

Nothing ->
    notFoundView

To dispatch a message that changes the route

Core View

view : Model -> Html Msg
view model =
    div []
        [ page model
        ]

page : Model -> Html Msg
page model =
    case model.route of
        PlayersRoute ->
            listView model.playersModel.players

        PlayerRoute id ->
            editView model id

        NotFoundRoute ->
            notFoundView

Players View

editView : Model -> PlayerId -> Html Msg
editView model id =
    case model.playersModel.players of
        NotAsked ->
            text ""

        Loading ->
            text "Loading ..."

        Failure err ->
            text (toString err)

        Success players ->
            let
                maybePlayer =
                    players
                        |> List.filter(\player -> player.id == id)
                        |> List.head
            in
                case maybePlayer of
                    Just player ->
                        core player

                    Nothing ->
                        notFoundView

Thank you in advance for any help!


Solution

  • It is possible to match on contents of sub models and see if it is an error route, before passing the message down to the edit view. So, the core page function would look like this.

    page : Model -> Html Msg
    page model =
        case model.route of
            PlayersRoute ->
                listView model.playersModel.players
    
            PlayerRoute id ->
                case model.playersModel.players of
                    Failure err ->
                        errorView err
    
                    _ ->
                        editView model id
    
            NotFoundRoute ->
                notFoundView
    

    Another option would be using full routes, and use the Navigation package. But, that would involve the overhead of setting up routes for each page. You would be able to send a Cmd to change the page at the top level of the app. Changing the Url would then cause a completely different page to be rendered.