Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a parent msg from component in Elm?

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.

like image 809
Eugene Matveyev Avatar asked Dec 15 '22 04:12

Eugene Matveyev


1 Answers

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.

like image 167
halfzebra Avatar answered Dec 22 '22 07:12

halfzebra