elm-architecture 单独的信号处理?

elm-architecture separate signal handling?

我在网上找不到任何示例来回答这个问题:parent 组件如何响应来自 child 模块的不同操作? 考虑带有提交按钮的简单聊天消息输入:

// child 组件:带提交按钮的文本输入

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" ]
        ]

持有此输入框的 parent 组件如何响应可能从该输入框发出的两个动作?传统的 "just passing through" 看起来像这样:

// parent 组件,保留帖子列表和 child 组件

-- 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 ]

我想要做的是捕获来自该 child 组件的消息并在正常 "just passing through" 之外响应它。像这样:

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
            }

但我不知道该怎么做,特别是因为在将地址转发给 child 时,您没有机会提供多个地址...

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

有两条主要途径可以做到这一点。

  1. 您可以将 MessageBox update 的签名更改为 return 您提供给 MessageBox init 的父操作 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 }