-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Forward all events (on:*) #8356
Conversation
@adiguba is attempting to deploy a commit to the Svelte Team on Vercel. A member of the Team first needs to authorize it. |
on:event={handler} with null|undefinedActually when the handler is null|undefined, it's still added to Buf if we use some modifiers (preventDefault, trusted, self, stopPropagation) that wrap the handler, it will be executed and they raise an exception. REPL : https://svelte.dev/repl/2a222085ea2f4f25a2a04e1955608536?version=3.55.1 Actually, this code : <a on:click|preventDefault={handler} ...> Will actually generate a code like this : dispose = listen(a, "click", prevent_default(/*handler*/ ctx[0])); In order to fix that, I make two change.
So this code will compile to : dispose = listen(a, "click", /*handler*/ ctx[0], [prevent_default]); |
on:event={handler} with dynamic handlerA similar problem can occur when the handler is dynamic, given that it is wrapped in a function that will do the null-check. So when <a on:click|preventDefault={handler} ...> Will actually generate a code like this : dispose = listen(a, "click", prevent_default(function () {
if (is_function(/*handler*/ ctx[0])) /*handler*/ ctx[0].apply(this, arguments);
})); With nothing on the I changed that with a new lifecycle function
When the component is mounted, the handler is registered like this : dispose = listen_and_update(
() => /*handler*/ ctx[0],
(h) => listen(a, "click", h, [prevent_default])
);; But the if (dirty & /*handler*/ 1) {
dispose.p(/*handler*/ ctx[0]);
} In fact the listen_and_update() method is pretty simple : export function listen_and_update(
get_handler: ()=>EventListenerOrEventListenerObject | null | undefined | false,
factory: (handler:EventListenerOrEventListenerObject | null | undefined | false) => Function) {
let handler = get_handler();
let dispose_handle: Function = factory(handler);
const dispose = () => dispose_handle();
// update :
dispose.p = () => {
const new_handler = get_handler();
if (new_handler !== handler) {
dispose_handle();
handler = new_handler;
dispose_handle = factory(handler);
}
};
return dispose;
} |
Event modifier on component's eventActually the component's event only accept the I've modified that in two way :
The function |
Forwarding (on:event)Actually when we forward an event, it adds a listener on the node which will take care of bringing it up via the component's dispatcher. So in fact the following code : <button on:click> click </button>
is compiled to this (like any other event handler) : dispose = listen(button, "click", /*click_handler*/ ctx[0]); Except that the handler is generated by the Svelte compiler : function click_handler(event) {
bubble.call(this, $$self, event);
} Where The major problem with this solution is that it adds a listener anyway, even if there is no handler on the component. I've completly rewrote that and now dispose = bubble($$self, listen, button, "click"); This method is mainly used to store info in the internal object It take 4 required arguments :
Finally, |
Forwarding all events (on:*)The same principle will be applied to on:*, which will be compiled to : dispose = bubble($$self, listen, node, "*"); But "*" will have a special meaning for So any event handler added to the component will be registered to the node. |
AliasIn fact this new So something like dispose = bubble($$self, listen, button, "click", "clicked"); This require a special case for This may cause a breaking change as actually event's modifier can be use when forwarding (but I dont see real use-case for that, and I think it should be forbidden). |
onEventListener()All of this is managed via a new lifecycle method onEventListener(), with the following signature : function onEventListener(
type: string,
fn: (
callback: EventListener,
options: boolean | AddEventListenerOptions | EventListenerOptions | undefined,
type: string
) => Function | undefined
) => void
When a new valid handler is added to the component (via
This is the heart of how on:* works, but could also be used to handle external events (like message from WebSocket...). |
A few remarks...Non-breaking change on internal (non-public) function.
Breaking change
TODO
Thanks for reading, and sorry for my poor english... |
Hello, There a lot of conflict with Svelte 4, so I just created another branch here : https://github.com/adiguba/svelte/tree/on-any-svelte4 |
Hello, I just created a little stackblitz.com with an inegrated svelte library that use Can be tested here : https://stackblitz.com/edit/on-any-4-svelte?file=src%2FApp.svelte |
I think that this PR is obsolete with Svelte 5. |
Hello,
I make a prototype that implement event forwarding via
on:*
, described in issue #2837In order to work it require some rewrite of the way Svelte manager the event's listener.
The main changes are as follows :
on:event={handler}
remove the listener when handler is null/undefined/invalid.on:event|modifiers={handler}
on component accepts the same modifiers as HTML node.on:event
add a listener on the node/component only when it's listened on the component.on:event|alias
allows us to forward an event under another name (alias).on:*
allow us to forward all events. In fact when any handler is registered to the component, it will be added to the sub node/component.on:*|prefix-*
oron:*|*-suffix
will allows us to forward alls events using a prefix or suffix.This will allow us to distinctly forward all event from distinct node/component.
onEventListener
that allows to manually manage an handler.There are still some jobs required (including tests and docs), and I have problems on some type-checking error with Components.
I will add comments here with details about my implementation.
Before submitting the PR, please make sure you do the following
feat:
,fix:
,chore:
, ordocs:
.Tests
npm test
and lint the project withnpm run lint