Currently there is no out of the box way to support DOM Custom Events in phoenix. I’ve created a separate library to do this, but I’d love to see it no longer needed In taking a gander at the LV js code, it seems to me that a good way to support this would be to:
allow a customEvents property to specify a single or array of custom event names on the dom option to the LiveSocket constructor.
the bindTopLevelEvents (i think?) would then need to add bindings for each of the specified custom event names
combined, this should allow the user to do phx-mycustomevent="mycustomevent" in their code
I would propose the payload for it to be the detail property of the custom even along with any phx-value-s merged in.
Possibly, the dataset of the target element could be merged in. This is what the existing library does
If there is consensus this is at least worth exploring, I’d be happy to have a go at a PR
I do think LV should support this ootb, but I’m not sure if I’d like having a new LiveSocket attribute. That would make adding libraries using that feature more painful to setup as they’d always need to tell the user what events to add to their LiveSocket constructor.
We can add the event listeners in morphdom’s onNodeAdded callback, but we should not need to iterate over all element’s attributes, so ideally it should be a simple el.hasAttribute && el.hasAttribute("phx-custom-event") or similar.
The main issue is then of course how to define multiple custom events or get a good mapping of custom event names to phoenix events.
Yeah that’s the trade-off, you either have to tell phoenix what custom events can possibly occur, or you have to iterate over the attributes.
This could make sense. At that point it gets kinda close to what my hook does, it would be pretty close to just merging the custom event hook into LV itself. But not having people need to add in the npm and hook themselves to use custom events would be a win for sure. The only concern I can think of is that you have to make the assumption that any attribute that starts with phx- that isn’t known by LV is a custom event. I wonder if some more specific prefix like phx-ce- or something isn’t better. A few more characters of typing but it’s more clear I think.
I was hoping to hear a little more feedback before I picked an approach and attempted a PR, but maybe it just makes sense to dive in.
Had another thought just now. What if the phx-custom-events attribute took a value that could be:
An event name like MyCustomEvent would cause phoenix to listen for the event and push it up the channel
An event name and handle_event name separated by : eg MyCustomEvent:custom_event would cause it to listen for the DOM custom event MyCustomEvent but send it to LV as custom_event
A comma separated list of the above
This would mean we only have a single attribute to look for, and the usage is a tad less onerous.
I don’t like solutions that rely on magic separators like : or ,, because they always restrict what event names can be accepted. As I don’t think there are any restrictions on event names, I can already see bug report incoming from someone, e.g., having a new CustomEvent("foo:bar"). This is of course also an issue with phx-ce-foo="bar" as not all symbols are allowed in attribute names…
Maybe instead we could do a <my-custom-element phx-custom-events={JS.custom_event_mapping(%{"foo:bar" => "foo-bar"})}>. That is then json encoded and decoded on the client. That would have no restrictions on event names. I’m not saying that the JS module is the perfect place for this, it’s just to demonstrate the idea.
Yeah you are right about separators being problematic. I agree with you about : but it is harder to imagine anyone making a custom event with a , in the name. I think another option is to be a bit smart and parse as json only if you have a { in the attribute value, otherwise fall back to something simplistic like a comma separated list. My guess it the simple case is what you need 99.99% percent of the time.