Beginner question on how to handle child component's event in parent

What I’m trying to do:
I’d like to handle an event that a custom component raises in the parent component.

What I’ve tried and what’s not working:
In the child component, I’ve tried having the button raise an event like this
button.raise_event('x-foo-event')
as well as doing an intermediary “click” event

self.button.set_event_handler('click', self.raise_foo)
...
def raise_foo(self, **event_args):
    self.button.raise_event('x-foo-event')

In the parent form, I’ve tried using an set_event_handler on the form
self.set_event_handler('x-foo-event', self.handle_event)
and by setting an event_handler on the child component
comp.set_event_handler('x-foo-event', self.handle_event)

handle_event doesn’t seem to run in any of the combinations I tried.

From what I understand, events should be the best way to have forms communicate with one another. My guess is I’m missing some small piece here.

I’ve tried searching the Q&A forum for questions like “event handled in different form” and haven’t been able to find answers. If you have suggestions for better keywords when searching the forum, I would super appreciate that as well :slight_smile:

Clone link:

Here’s a fixed version:

There were several interconnected issues that needed to be fixed:

  1. You have to register the event in the Custom Component window here:
  2. You need to raise the event in the custom component from self (that is the custom component itself), not from the button – raising it from self from the button click handler was a good choice though!
  3. You need to register the event on the component that is in the form, not the parent form itself (so comp in your example, not self)
1 Like

Just a few more things:

You don’t nedd to register the event in the custom components properties if you will use an event that starts with x-. This prefix is made so you don’t need to do it. Also, if you register the event in the custom component, you don’t need to make your event start with x-. So you can either raise x-foo-event for an event that is not registered (or most of the time from a component that is not custom and you can’t control their events) or register and raise a foo-event. If this is your custom component and you will ofter trigger it, the best approach is obviously using a registered event, since it’s clearer.

As @duncan_richards12 said, you need to raise the event from the form itself, not the button. raise-event sends a message to the component’s parent. So self.button.raise_event('x-foo-event', value=foo) just sent foo to self. One thing that is good to check if it’s correctly sending to where you want is to check who is the component’s parent property, since he is who is getting the event. This is useful because if you did this, you would see that self.button.parent was <CustomComponent>, not <Form>, but self.parent was <Form> as expected.

This is important specially when you want a datagrid row to comunicate with the main form. The most intuitive thing to think (at least to me it was) is to bind the event_handler to the grid itself, but the parent of each row is the repeating_panel inside the data_grid. And I just realized this when I printed the self.parent inside a DataGridRow.

1 Like

It might also be worth knowing that we (the anvilistas team) have a couple of other approaches to this problem:

The Anvil Extras messaging module

The Anvil Reactive dependency

Speaking personally, I don’t like having event handling so closely coupled with the ui component structure.

2 Likes

Thank you for helping me understand how these forms communicate with each other. It seems like events are meant for communicating between parent form and child form, is that an accurate understanding? The message module that @owen.campbell mentions is the tool that allows arbitrary forms to communicate with each other.

Just for my own curiosity - what does “anvilistas” mean? I know there is a fantastic collection of libraries oh github under that org. My brain keep parsing it as “Anvil is T[ool] A[ssisted] S[peedrun]” and I doubt that’s it.

Events are much more general than that. It’s a general mechanism for de-coupling caller and callee, so that

  • Callees can be any function, anywhere.
    • Caller does not have to know the callee in advance.
  • Callees can come and go as needed.
    • They might not even exist right away.
  • Any number of callees can receive the signal/event, in some order.
    • Each one responding in its own way, without having to know anything about the other responders.

Just so I understand fully… Events are for parents and children components to communicate.

Even if the callee can be any function or in any form, raise-event only ever sends a message to the parent and raise_event_on_children only ever sends the message to direct children of the component.

Does that sound right?

raise_event raises the event to the owner of that methods.

If you call a.raise_event, then a will receive the message. If you call b.raise_event, then b will receive the message.

raise_event_on_children works only in repeating_panels. It’s the same as iterating each row and calling raise_event on each of them.

2 Likes

It raises the event on whichever component you choose. In your case, the parent is the appropriate component. That is probably the most common case, but certainly not the only one.

In more deeply-nested cases, it might be an ancestor further back, or a “cousin”. It’s not hard to imagine notifying some other component entirely, if that’s your “designated dispatcher” for that event.

2 Likes

Thank you both for the explanation! This makes a lot more sense to me now.

1 Like

To add on, events are also for triggering certain actions within a component: for example a.raise_event might trigger a color change for a and likewise if it were for b.raise_event

1 Like