Отдельная обработка сигналов в архитектуре elm?

Я не могу найти нигде в Интернете пример, который отвечает на вопрос: как родительский компонент реагирует на различные действия, исходящие из дочернего модуля? Рассмотрим простой ввод сообщения чата с помощью кнопки отправки:

// дочерний компонент: ввод текста с кнопкой отправки

type Action
    = InputChanged String
    | MessageSent String


view : Signal.Address Action -> Model -> Html
view addr model =
    div []
        [ input
            [ type' "text"
            , value model.content
            , on "input" targetValue (\val -> Signal.message addr (InputChanged val))
            ]
            []
        , button
            [ type' "submit"
            , onClick addr (MessageSent model.content)
            ]
            [ text "Send" ]
        ]

Как родительский компонент, удерживающий это поле ввода, реагирует на два действия, которые могут выходить из этого поля ввода? Традиционное "просто прохождение" выглядит так:

// родительский компонент, содержит список сообщений и дочерний компонент

-- update

type Action
    = MessageBoxAction MessageBox.Action


update : Action -> Model -> Model
update act model =
    case act of
        MessageBoxAction msg ->
            { model |
                currentMessage = MessageBox.update msg model.currentMessage
            }

-- view

view : Signal.Address Action -> Model -> Html
view addr model =
    div []
        [ MessageBox.view (Signal.forwardTo addr MessageBoxAction) model.currentMessage ]

То, что я хочу сделать, это перехватить сообщение, исходящее от этого дочернего компонента, и ответить на него сверх обычного "просто прохождения". Что-то вроде этого:

case act of
    MessageBoxSubmit msg ->
        let updatedMessage = MessageBox.update msg model.currentMessage
            newPost = Posts.update msg model.posts
        in  
            { model |
                posts = model.posts :: [ newPost ]
                , currentMessage = updatedMessage
            }

Но я понятия не имею, как это сделать, особенно потому, что при переадресации адреса ребенку у вас нет возможности предоставить более одного адреса...

MessageBox.view (Signal.forwardTo addr MessageBoxAction) model.currentMessage

1 ответ

Решение

Для этого есть два основных пути.

  1. Вы можете изменить подпись MessageBox update вернуть родительское действие, которое вы предоставляете в MessageBox init,

    init : (String -> parentAction) -> Model
    init onSend = 
      { onSend = onSend
      , content = "" 
      }
    
    update : Action -> Model -> (Model, Maybe parentAction)
    
    update action model =
      case action of 
        MessageSent msg -> 
           let 
             model' = ...
           in 
             (model', Just (model.onSend msg))
    
        InputChanged str -> 
           let 
             model' = ...
           in 
             (model', Nothing)
    

и в родительском модуле вы делаете:

init = 
  { posts = [] 
  , currentMessage = MessageBox.init HandleSent
  }

update : Action -> Model -> Model
update act model =
    case act of
        MessageBoxAction msg ->
          let 
            (currentMessage', send) = MessageBox.update msg model.currentMessage
            model' = {model | currentMessage = currentMessage'} 
          in
            case send of 
              Nothing -> model'
              Just act -> update act model' -- you recursively call the update function with the new action that you received from the MessageBox.update
        HandleSent str -> { model | posts = str::model.posts }           
  1. Вы можете предоставить декодер для действия в модуле MessageBox.

в модуле MessageBox

sentMessage action = 
  case action of
    MessageSent msg -> Just msg
    _ -> Nothing

в родительском

update : Action -> Model -> Model
update act model =
    case act of
        MessageBoxAction msg ->
          let 
            currentMessage' = MessageBox.update msg model.currentMessage
            model' = {model | currentMessage = currentMessage'} 
          in 
            case MessageBox.sentMessage msg of
              Nothing -> model'
              Just str -> update (HandleSent str) model'

        HandleSent str -> { model | posts = str::model.posts } 
Другие вопросы по тегам