榆木0.18:如何使用Sub.map根据内容路由消息?

时间:2017-01-03 19:18:25

标签: elm

我已经动态创建了通过端口与Elm模型进行通信的Javascript组件。他们通过port sendData : ((ComponentId, String) ...

格式的端口发送数据

在榆树方面,有一个ChildComponent模块代表他们的模型:

type alias ComponentId =
  Int

port sendData : ((ComponentId, String) -> msg) -> Sub msg

type Msg
  = ProcessData String

subscriptions : Model -> Sub Msg
subscriptions model =
Sub.batch [
  sendData ProcessData
]

Parent我有:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  case msg of
    ChildMessage componentId msg -> ...

subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.batch
    [ Sub.map (ChildMessage componentId) (ChildComponent.subscriptions model.child) ]

显然这会导致

Cannot find variable `componentId`

310|     Sub.map (ChildMessage componentId) (ChildComponent.subscriptions (getChildModel (componentId model))) ]
                               ^^^^^^^^^^^

如何从端口数据中“提取”componentId?或者也许我正在做的是完全错误的?

更新

我已将问题(由@ChadGilbert建议添加一些内容)分离到项目中https://github.com/lguminski/ElmMultipleComponentsSubscription

1 个答案:

答案 0 :(得分:0)

使用Sub.map时,您需要一个接受子模型作为参数的函数。您可以将ChildMessage定义为

-- in Parent.elm
type Model =
    ...
    | ChildMessage Child.Model

您的父订阅功能需要更新:

-- in Parent.elm
subscriptions model =
    Sub.batch
        [ Sub.map ChildMessage (ChildComponent.subscriptions model.child) ]

当涉及从子端口处理数据时,您需要一个Msg,其第一个参数与端口中定义的类型相同,主要是(ComponentId, String)。考虑向您的孩子添加这样的内容,然后新的RouteData消息可以在需要时调用ProcessData更新案例,否则忽略它。

-- in Child.elm
type Msg
  = ProcessData String
  | RouteData (ComponentId, String)

subscriptions : Model -> Sub Msg
subscriptions model =
Sub.batch [
  sendData RouteData
]

update msg model =
    case msg of
        RouteData (componentId, val) ->
            if componentId == "my-component-id" then
                update (ProcessData val) model
            else
                model ! []
        ProcessData val -> ...

<强>更新

根据您的其他详细信息,我建议您将端口和子Msg路由移出Child模块并进入Parent。然后,您可以将以下更新案例添加到父级:

ChildMessage componentId childMessage ->
    let
        childUpdates =
            List.map (\c -> if c.id == componentId then updateChild c else c ! []) model.children

        updateChild child =
            let
                (updatedChildModel, cmd) =
                    Child.update childMessage child
            in
                updatedChildModel ! [ Cmd.map (ChildMessage child.id) cmd ]

    in
        { model | children = List.map Tuple.first childUpdates }
            ! (List.map Tuple.second childUpdates)

RouteData (componentId, val) ->
    update (ChildMessage componentId (Child.ProcessData val)) model

I have created a pull request for your repository here