Communicating with a parent component to relay something happened in a sub component

516 views
Skip to first unread message

Mike Gehard

unread,
Dec 6, 2015, 8:40:29 PM12/6/15
to Elm Discuss
Hello all,

Thanks for all of the hard work on Elm and all of the great answers here.

I have a question about using the full Elm Architecture (update returns a model and an effect) when I have a component that has a sub component.

I am building out a Minesweeper game (https://github.com/mikegehard/elm-minesweeper) that has a Game component that contains a Board component.

Within the Board update function, I need to tell the enclosing Game component that the user clicked on a mine (https://github.com/mikegehard/elm-minesweeper/blob/master/src/minesweeper/Board.elm#L33). I am wondering how I would go about that.

My initial thoughts are to return an Effect that is a Task.fail and but I can't figure out how to get that to map into an Action on the Game (https://github.com/mikegehard/elm-minesweeper/blob/master/src/minesweeper/Game.elm#L16 for the current Game actions) so that the Game.update function would catch it and mark the Game as Lost (https://github.com/mikegehard/elm-minesweeper/blob/master/src/minesweeper/Game.elm#L19 for the current Game.update).

I am sure I'm missing something simple here with how this all gets wired together as I'm just fully learning Elm and how Effects work.

Any help figuring out how to let the Game know that a user clicked a mine would be much appreciated.

Thanks in advance!

Mike

Mike Gehard

unread,
Dec 6, 2015, 8:47:06 PM12/6/15
to Elm Discuss
I have also pushed a branch for the start of this work here:

Max Goldstein

unread,
Dec 6, 2015, 9:58:44 PM12/6/15
to Elm Discuss
return an Effect that is a Task.fail

Usually, a failed task means an actual error (e.g. HTTP 404), not something that is "bad" for the user but a state that can reasonably be expected (e.g. search returned no results, user not logged in, or player lost the game).

What's happening in your WIP is that the failure will get ignored somewhere, since Effects.task only takes tasks that Never fail. Adding the HitMine tag to Game is on the right track, but as you have it it will never get created.

I *think* the best way to do this is:

-- Game.Elm
mineClicked : Mailbox ()
mineClicked = Signal.mailbox ()

Then pass mineClicked.address to Board.create and keep it in the model. And you also need to thread Signal.map (always HitMine) mineClicked.signal in as an input in the StartApp config. It's kind of icky.

Subcomponents pass up Html and Effects to their supercomponent, but in both cases they're somewhat opaque: the Html is placed as the child of more Html, and the effects are merged in and passed up, ultimately to the runtime. The root of the problem is that the subcomponent needs to communicate with the the super information that doesn't really fall into either of those categories.

Magnus Rundberget

unread,
Dec 7, 2015, 1:23:15 AM12/7/15
to Elm Discuss
Out of curiosity (since I'm also one of the many beginners that have implemented a minesweeper in elm);

Why can't you just derive loss/win/whatever by the state (/aka model) of the game rather than reacting to a click. Unless there is some particular reason for it (maybe some animation or whatever) I think you will find your solution gets way way simpler if you liberate yourself from an mvc event- bubling/pingpong kind of thinking.

Maybe I'm missing something ?

cheers
Magnus

Evan Czaplicki

unread,
Dec 7, 2015, 1:57:01 AM12/7/15
to elm-d...@googlegroups.com
I think folks take the Elm Architecture tutorial too literally. It's more of guidelines. If you want to pass information to a parent after an update, just do it:

update : Action -> Model -> ( Model, Effects Action, whateverYouWant )

I do this with a little slider I am creating at the moment.

Hopefully that's a helpful perspective!

--
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 elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dave Keen

unread,
Dec 7, 2015, 3:54:27 AM12/7/15
to Elm Discuss
I've also struggled with the same problem in another thread and we came up with a solution that involves passing some extra parameters into the child's view; basically allowing the parent to expose a strongly typed api to the child. The discussion is at the end of https://groups.google.com/forum/m/#!topic/elm-discuss/xrmWjVobMcA

I'm a little bit worried that this is an anti-pattern for some reason that I can't quite grasp, so I'd appreciate opinions on it!

Max Goldstein

unread,
Dec 7, 2015, 10:28:12 AM12/7/15
to Elm Discuss
I think Magnus's and Evan's solutions are both better than mine.

Regarding the Board being inspected by the Game, my OO training asks, is it the Board's business whether something happened in the Game? And maybe it's not, so you could export a function in Board to put the logic there. Game asks Board if something happened, and if so, responds according to its own needs. I think you could even make/keep the Board opaque this way. That said, leaking model types isn't bad when you're not writing a library, since the compiler will catch everything and no one else is relying on the old definition.

Mike Gehard

unread,
Dec 7, 2015, 1:58:00 PM12/7/15
to Elm Discuss
Thanks for the reply Magnus.

I was thinking about putting the state in the board model so that the game could simply query the model to determine what to display. I am still playing with this whole hierarchy and wanted to get some more background/discussion here as well.

Mike Gehard

unread,
Dec 7, 2015, 2:01:57 PM12/7/15
to Elm Discuss
This is great perspective Evan. Thanks for sharing it!

I'm working on a blog post that outlines some thoughts around multicomponent apps in Elm and I'll make sure I spread it far and wide. :-)

d13

unread,
Dec 29, 2015, 5:14:50 PM12/29/15
to Elm Discuss
I recently had to do something similar and ended up using a Context in the child module:


But after discovering this thread, I believe my solution is too complex and am going to refactor this based on Evan's recommendations.
If would greatly appreciate any suggestions on how my code could be simplified. 
Reply all
Reply to author
Forward
0 new messages