fraxture
fraxture

Reputation: 5530

How to integrate multiple msg types from different elm features into a top-level app

I have a feature called Editor that I'm trying to plug into my app. Its view returns a the type Html EditorMsg. I plug it in here:

edit : Editor.Types.Model -> Html EditorMsg
edit editorModel =
    div [ class "edit" ] [ Editor.view editorModel ]

In the app, I have routing and so edit is called by way of my main view function, plus a couple of functions that manage which route to show:

-- VIEW


view : Model -> Html Msg
view model =
    div []
        [ RoutingMsg navBar
        , EditorMsg (render model)
        ]


render : Model -> Html EditorMsg
render model =
    case model.route of
        Nothing ->
            li [] [ text "Invalid URL" ]

        Just route ->
            showRoute route model


showRoute : Route -> Model -> Html EditorMsg
showRoute route model =
    case route of
        Home ->
            home

        Editor ->
            edit model.editor

My view also contains a navBar as you can see, and that returns Html possibly containing a different type of message, RoutingMsg:

navBar : Html RoutingMsg
navBar =
    NavBarState.config
        |> NavBarState.items
            [ navItem "/" "Home" False
            , navItem "/editor" "Edit" False
            ]
        |> NavBar.view

This setup didn't compile because in view I have a list of two different types, one returning Html RoutingMsg and the other Html EditorMsg, so I set up a union type to try to contain that difference:

type Msg
    = RoutingMsg (Html RoutingMsg)
    | EditorMsg (Html EditorMsg)

This seems to work, but then I run into trouble in my update method, where the matching doesn't quite work. Long and short of it is that it feels as though I've just got the wrong pattern. My goal was to make my Editor somewhat independent, like a module, that can be plugged in different places. But it's hard for me to understand in Elm, how to integrate things.

To illustrate the problem more simply, I created this example on ellie-app that approximates what I tried to do but can't get working: https://ellie-app.com/PKmzsV3PC7a1/1

Is my approach here just incorrect, or if not, how can I get this code to work?

Upvotes: 1

Views: 868

Answers (1)

halfzebra
halfzebra

Reputation: 6807

You should use Html.map to map children messages to the top-level Msg

Here's what you've been missing:

view : Model -> Html Msg
view model =
    div []
        [ Html.map ButtonDecMsg buttonDec
        , div [] [ text (toString model) ]
        , Html.map ButtonIncMsg buttonInc
        ]

Also the type annotation definition of child update functions should include the message type:

buttonDecUpdate : ButtonDecMsg -> Model -> Int
buttonDecUpdate msg model =
    model - 1

Here is an example of working app: https://ellie-app.com/PM4H2dpFsfa1/0

Upvotes: 5

Related Questions