Here is a quick sketch (untested) of a generic container class for children
whose views support generating a remove message. I left out the logic for
passing commands up through update for brevity:

type alias Model childModel =
  { nextId : Int, children : List (Int, childModel) }


type Message childModel childMessage
  = Child Int childMessage
  | Remove Int
  | Append childModel


type RemovableMessage childMessage
  = ToThis childMessage
  | RemoveThis


update :
  (childMessage -> childModel -> childModel)
  -> Message childModel childMessage -> Model childModel -> Model childModel
update childUpdate message model =
  case message of
    Child id childMessage ->
      { model
      | children =
          model.children
          |> List.map (\((childId, childModel) as entry) ->
                if childId == id then
                  (childId, childUpdate childMessage childModel)
                else
                  entry)
      }
    Remove id ->
      { model
      | children = List.filter (fst >> ((/=) id)) model.children
      }
    Append child ->
      { model
      | nextId = model.nextId + 1
      , children = List.append model.children [ ( model.nextId, child ) ]
      }


view :
  (childModel -> Html.Html (RemovableMessage childMessage))
  -> Model childModel
  -> Html.Html (Message childModel childMessage)
view viewChild model =
  Html.div [] <| List.map (viewEntry viewChild) model.children


viewEntry :
  (childModel -> Html.Html (RemovableMessage childMessage))
  -> (Int, childModel)
  -> Html.Html (Message childModel childMessage)
viewEntry viewChild (childId, childModel) =
  Html.App.map (translateRemovableMessage childId) <| viewChild childModel


translateRemovableMessage :
  Int -> RemovableMessage childMessage -> Message childModel childMessage
translateRemovableMessage childId removableMessage =
  case removableMessage of
    ToThis childMessage -> Child childId childMessage
    RemoveThis -> Remove childId


The RemovableMessage type essentially serves as our interface to the child
view function and could in fact be part of the declaration for that
function or stored elsewhere as a generally useful concept that might work
with multiple container and contained types.

This case is arguably cleaner than the Elm 0.16 solution. On the other
hand, it's going to get a bit messy if one has to pass values up through
several layers. In Elm 0.16, that could be handled by providing a
Signal.Message to handle the message that needed to go somewhere other than
the "corresponding" model. But maybe the strength here is in putting some
separation between the model hierarchy and the view hierarchy and weakening
the notion that there is a one-to-one correspondence all the way down.

Mark

On Sat, Jul 9, 2016 at 2:27 PM, Mark Hamburg <[email protected]> wrote:

> On Jul 7, 2016, at 6:22 PM, Alex Lew <[email protected]> wrote:
> >
> > I wrote about a generalization of that pattern a week or so ago, in case
> you're interested:
> https://medium.com/@alex.lew/the-translator-pattern-a-model-for-child-to-parent-communication-in-elm-f4bfaa1d3f98
>
> I like this approach. It emphasizes that the tagger can be a somewhat more
> arbitrary translator function — something that common usage patterns
> obscure. Furthermore, it could be extended to reflect the fact that the
> translator needed for update results might be different from the translator
> needed for view results. In particular, the view function in the counter
> example can be typed as returning an Html (Removable.Msg Counter.Msg) for
> which it is then the job of the parent view to provide a translator into a
> parent message.
>
> Mark

-- 
You received this message because you are subscribed to the Google Groups "Elm 
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to