hopsoft / turbo_boost-commands

Commands to help you build robust reactive applications with Rails & Hotwire.
MIT License
322 stars 20 forks source link

How to register alternate events? #56

Closed drnic closed 1 year ago

drnic commented 1 year ago

In https://github.com/hopsoft/turbo_reflex/blob/main/README.md#reflex-triggers it suggests we can override default events with TurboReflex.registerEvent but this method doesn't exist for me.

I found TurboReflex.registerEventDelegate but I couldn't get it to register onmouseover or some other event types.

TurboReflex.registerEventDelegate('onmouseover', ['p[data-turbo-reflex]'])

Similarly, I tried inline custom events like data-turbo-reflex="onmouseover->CounterReflex#increment" but didn't see the reflex action trigger.

hopsoft commented 1 year ago

You'll want to remove the on prefix in the event name. Here's an example.

TurboReflex.registerEventDelegate('mouseover', ['p[data-turbo-reflex]'])
TurboReflex 2022-12-18 05-50-23

Let me know if this works for you.

hopsoft commented 1 year ago

Note that after the lib rebrand to TurboBoost Commands, this code should be changed to:

TurboBoost.Commands.registerEventDelegate(...)
adamjleonard commented 1 year ago

@hopsoft I wanted to follow up on this conversation. I've attempted to register a keyup, keydown and keypress event but unfortunately it didn't trigger the command.

I unfortunately don't have a sample project for you at the moment and I'll see if I can take this local and do some debugging myself, but just wanted to put it on your radar.

hopsoft commented 1 year ago

Interesting. If it helps, this is where/how the library registers the default event delegates. https://github.com/hopsoft/turbo_boost-commands/blob/main/app/javascript/index.js#L92-L98

And here's where the library identifies a match when the event is triggered. https://github.com/hopsoft/turbo_boost-commands/blob/8217e3925fab7cb0f0ca2198f39ef82732dca85b/app/javascript/delegates.js#L17

hopsoft commented 1 year ago

Verified that we definitely have a bug related to registering additional events. The click handler is taking precedence. Will dive into this shortly.

adamjleonard commented 1 year ago

I tested this current branch against keyup as well and that seems to still fail. I hope tomorrow to actually sit down and take a look at debugging.

EDIT

I was able to get this to work if I did the following

TurboBoost.Commands.registerEventDelegate("change", []);
TurboBoost.Commands.registerEventDelegate("click", []);

TurboBoost.Commands.registerEventDelegate("keydown", [
  "input[data-turbo-command]",
]);

but unfortunately this prevented any actual value being typed in to the input

adamjleonard commented 1 year ago

Excuse my stupidity but is there a specific reason to not use something similar to stimulus where you can bind the action with change->Command->method

hopsoft commented 1 year ago

Great question. Yes, there is a reason we opt for event delegation over per-element bound event listeners. Low level tools and libraries (like this one) can't make too many assumptions about the JavaScript ecosystem they're running within.

One example to illustrate the challenges is related to how StimulusReflex implicitly registers a StimulusJS controller for events that declare a data attribute that haven't already registered a StimulusJS controller with similar behavior. Detecting this is problematic because there's no way for the library to know how the JavaScript assets are organized or how they'll be loaded and parsed for a given application. This scenario is exacerbated with new techniques like import maps etc... Any client side Stimulus controller that's implicitly wired up can introduce unintentional and unwanted duplicate behavior (i.e. trigger RPC calls multiple times from a single client side event).

Event delegation eliminates this problem entirely, so it doesn't matter how the JavaScript assets are resolved, loaded, and parsed on the client. The primary caveat of delegation being that it limits us to bubbling events.

Having said that, event delegation also works with custom events and each registered delegate can include multiple selectors per event. Selectors for each delegate can be as simple or as sophisticated as desired.


In your example above, I would expect the following delegate to work.

const existingChangeDelegate = TurboBoost.Commands.eventDelegates.find(e => e.name === 'change')
TurboBoost.Commands.registerEventDelegate('change', [...existingChangeDelegate.selectors, '.my-element[data-turbo-command]'])

I could probably make this a bit more intuitive though.

hopsoft commented 1 year ago

Just created ticket #75 to help with event delegate overrides.