There are a bunch of ways to approach this! The most straightforward is to separate message handling for the parent and child. You may have already done this! So what remains is to fill in the data structure.
I’d use Array for this. Until 0.19, you’ll want to use Skinney/elm-array-exploration for this, as core has some bugs. You’ll use it a little like this:
module CounterList exposing (..)
import Counter exposing (Counter)
import Array.Hamt as Array
type alias CounterList =
Array Counter
type Msg
= CounterMsg Int Counter.Msg
update : Msg -> Model -> Model
update msg model =
case msg of
CounterMsg index counterMsg ->
model
-- let's look up the counter at that index
-- first. This will give us `Maybe Counter`.
|> Array.get indexFromMsg
-- next, we'll update the counter inside
-- the `Maybe`. I'm assuming `Counter.update`
-- has the same signature except with a counter
-- instead of `Model`. After this we still have
-- `Maybe Counter`.
|> Maybe.map
(\counter -> Counter.update counterMsg counter)
-- now let's set the updated counter inside our array.
-- after this, we have `Maybe (Array Counter)`.
|> Maybe.map
(\counter -> Array.set indexFromMsg counter model)
-- we need to unwrap our array from the Maybe, so we need
-- to provide a default. I'm just going to not care if we
-- were given a bad index, and return the original model.
-- you may want to do something different here.
-- If that's the case, stick this whole thing in a `let`
-- and pattern match on the `Maybe (Array Counter)`.
|> Maybe.withDefault model
If that doesn’t answer your question, could you share what you’ve got so far and we can work on it together?
This data transformation looks daunting at first, but give it a little time. These things quickly become second nature. The compiler will help you here, and so will we. 