Closed dariooddenino closed 5 years ago
Hi @dariooddenino! I'm sorry for the late reply; I've been on vacation in Japan for the past two days and will be here for another week before I'm back on a normal working schedule. I have an inkling of what might be wrong, but I haven't recreated your example. If my comments below don't help, let me know and I'll dig a little deeper tomorrow on the train.
The short version: I believe you need to handle the Formless
outputs, and specifically the Emit
message with the top-level component query inside.
The longer version (forgive me if this is not your issue!):
In your render function for the typeahead, you're triggering HandleSelect
when the "CLICK" button is clicked. Next, your eval
function for the typeahead kicks in, logging "Check" and then raising the message SelectionsChanged
.
What happens next? Well, messages are the outputs of a component which get handled by their parent, and the parent in this case is Formless.
Formless, unfortunately, has no idea what to do with a SelectionsChanged
message. But you know how to handle it, because you have a query written for exactly that: your top-level component's Typeahead
query. And Formless does know what to do with top-level component queries: it has the Raise
query and related Emit
message. You can take your top-level component's query type, wrap it up in Raise
(to make it a Formless query), and then Formless will send that query back to you via the Emit
message whenever it gets triggered.
You've done that in your code: your handler for the typeahead component inside Formless is (HE.input $ F.Raise <<< H.action <<< Typeahead)
. You wrap up the message in Typeahead
(your top-level component's query type), then wrap that up in Raise
(a Formless query type). Finally, this handler gets called.
The eval
function in Formless handles this new Raise
message. It unwraps it to get the top-level component query inside, then sends an Emit
message containing the query to your top-level component. So we repeat the pattern!
Now we're in your top-level component. It should handle the Emit
message from the Formless component by unwrapping it and evaluating the query inside (after all, the query inside is a top-level component query):
eval (HandleFormless (F.Emit query) a) = eval query *> pure a)
So we've unwrapped the query, which is Typeahead message a
, and called eval
on it. Now, finally, your eval
function in the top-level component will evaluate the Typeahead
query and logged out the message and the process is complete.
To learn more about how renderless components work, see this commented file from purescript-halogen-renderless
(I promise to write about the topic soon!):
https://github.com/thomashoneyman/purescript-halogen-renderless/blob/master/example/Child.purs
Ugh, it's that for sure. I spent 3 hours analyzing carefully all the examples, and I missed it all the time :( I'm gonna kill myself.
Thanks!
Don't worry, it's not an obvious thing! Renderless components are a fairly new idea in Halogen and nothing has been written about them, though I gave a talk at LambdaConf this year that explains the concept. With only two examples of embedded parent queries and no real documentation it's an easy thing to miss. Rule of thumb: any time you use a renderless component like Select or Formless, go ahead and write your eval case for Emit
first, handling the outputs recursively:
eval = case _ of
HandleFormless message a -> case message of
Emit query -> a <$ eval query
I'll add this to documentation soon :)
Is there a vod of the talk by any chance? :)
While it was recorded, the LambdaConf 2018 videos haven't been released yet. Sorry about that! They should be coming out at any moment now.
I'm not sure if I'm doing something wrong, but I can't manage to thread messages from external components to the parent. I get no errors, but just silent fails so I'm having a hard time figuring out what's wrong. I'm using version 0.2.0
Edit to add that if I build the examples they work correctly. There has to be something that I'm failing to see here (but that should give me an error :( )
I've reduced my code to a pretty simple case: