4

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.

glennsl
  • 28,186
  • 12
  • 57
  • 75
Eugene Matveyev
  • 161
  • 1
  • 2
  • 14
  • Take a look at the [Translator Pattern](https://medium.com/@alex.lew/the-translator-pattern-a-model-for-child-to-parent-communication-in-elm-f4bfaa1d3f98) by Alex Lew. It offers a somewhat clean solution to this problem. – Chad Gilbert Aug 08 '16 at 11:18
  • Just to make sure I get your question right: there is a single close button for all components or each has its own and you just want to share the logic? – Zimm i48 Aug 09 '16 at 08:58
  • @Zimmi48 Each has its own and I want to share. – Eugene Matveyev Aug 09 '16 at 15:54
  • @ChadGilbert Thanks, I'll try to use his ideas. Btw, it feels too complicated for a such common/simple task (or I just don't get it). – Eugene Matveyev Aug 09 '16 at 15:58

1 Answers1

12

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.

halfzebra
  • 6,771
  • 4
  • 32
  • 47