2017-01-03 10 views
0

У меня динамически создаются компоненты Javascript, которые обмениваются данными через порты с моделью Elm. Они посылают данные через порт формы port sendData : ((ComponentId, String) ...Elm 0.18: Как перенаправить сообщения с Sub.map на основе их содержимого?

На стороне вяза есть 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 из данных, поступающих из порта? Или возможно, что я делаю, это совершенно неправильно?

Update

Я изолировал проблему (с некоторыми дополнениями, предложенного @ChadGilbert) в проект https://github.com/lguminski/ElmMultipleComponentsSubscription

ответ

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

Update

Основываясь на ваших дополнительных деталей, я хотел бы предложить, что вы двигаетесь порт и ребенка Msg маршрутизацией из модуля ребенка и в 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

+0

спасибо! @ChadGilbert, мы почти там. Единственная проблема заключается в том, что существует несколько дочерних элементов, поэтому 'model.child' не может быть предоставлен заранее, как вы предложили. Он также должен быть рассчитан динамически на основе сообщения. Для иллюстрации этого я создал https://github.com/lguminski/ElmMultipleComponentsSubscription. –

+0

Я обновил свой ответ с небольшими изменениями в вашем дизайне, а также создал запрос на перенос вашего кода с рабочим примером. –

+0

Спасибо @ChadGilbert. Я вижу, что вы повысили привязку к ребенку (порт и подписки) до уровня родителя и обрабатываете его там. На самом деле это то, чего я пытался избежать, поскольку я воспринимаю это как утечку абстракции ребенка. Я надеялся, что есть способ инкапсулировать все это в Child. Но, возможно, это невозможно. Позвольте моему вопросу быть открытым в течение нескольких дней, и если никто не придет к лучшей идее, я буду принимать вас в качестве действительного обходного пути. Еще раз спасибо. –