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.