Отдельная обработка сигналов в архитектуре 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 ответ
Для этого есть два основных пути.
Вы можете изменить подпись MessageBox
update
вернуть родительское действие, которое вы предоставляете в MessageBoxinit
,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 }
- Вы можете предоставить декодер для действия в модуле 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 }