How to call a parent msg from component in Elm?

1.9k Views Asked by At

I have a modal window that can display different components inside it. Each component has it's own updater and messages, but I want to share a close button between them.

Thus I can't call "CloseModal" directly from my children — Elm doesn't allow me to call someone else messages. What are my options?


I thought I could call "Modal.Update.update Modal.Messages.CloseModal", but inside my components I have only chunks of a state. So it's not a option.

Then I found a way to pass messages from parent to child, but it doesn't help me to pass messages other way around. Or to siblings.

1

There are 1 best solutions below

0
On BEST ANSWER

In short, you can not pass messages directly from child to parent or a sibling.

Elm Architecture implements uni-directional message passing, in other words, your parent component is always aware of messages for child components before child component will receive a message.

I have made a simple example of parent-child communication, it is way too big to embed it into an answer so that I will note only the key points here.

Child

Child component defines a set of Messages:

type Msg
    = Update Model
    | Focus
    | Blur

In it's update function we ignore Messages, intended for the parent component.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Update value ->
            ( value, Cmd.none )

        -- Ignore the rest of the messages.
        _ ->
            ( model, Cmd.none )

Parent

In parent's update function we can pattern match required messages and react to them.

The rest of the messages will go through default branch.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        NameMsg childMsg ->
            case childMsg of
                {- We have intercepted a message from child component.
                   This part of the update function might be moved
                   to a separate function for better readability.
                -}
                Input.Focus ->
                    update (HelperMsg Helper.Show) model

                Input.Blur ->
                    update (HelperMsg Helper.Hide) model

                -- The default message passing routine.
                _ ->
                    let
                        ( nameModel, nameCmd ) =
                            Input.update childMsg model.name
                    in
                        ( { model | name = nameModel }
                        , Cmd.map NameMsg nameCmd
                        )

The example above concludes the child-parent and sibling communication. You can run the update function recursively as much as you want with any messages to any components.

Sending Messages from child's update function

Cmd.Extra exposes a function for sending messages.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model ->
    (model, message SomeMessage)

PS: Translator pattern example is on my To-do, leave a comment if you want me to update the answer with it.