diff --git a/src/compiler/compile/nodes/InlineComponent.ts b/src/compiler/compile/nodes/InlineComponent.ts index 2c8265ade1..6db6f1b327 100644 --- a/src/compiler/compile/nodes/InlineComponent.ts +++ b/src/compiler/compile/nodes/InlineComponent.ts @@ -100,6 +100,17 @@ export default class InlineComponent extends Node { this.scope = scope; } + this.handlers.forEach(handler => { + handler.modifiers.forEach(modifier => { + if (modifier !== 'once') { + component.error(handler, { + code: 'invalid-event-modifier', + message: `Event modifiers other than 'once' can only be used on DOM elements` + }); + } + }); + }); + this.children = map_children(component, this, this.scope, info.children); } } diff --git a/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts index d2d76b41de..696fff51c0 100644 --- a/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render-dom/wrappers/InlineComponent/index.ts @@ -363,7 +363,9 @@ export default class InlineComponentWrapper extends Wrapper { }); const munged_handlers = this.node.handlers.map(handler => { - const snippet = handler.render(block); + let snippet = handler.render(block); + if (handler.modifiers.has('once')) snippet = `@once(${snippet})`; + return `${name}.$on("${handler.name}", ${snippet});`; }); diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 7802c69b20..152c0e79b0 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -81,6 +81,15 @@ export function exclude_internal_props(props) { return result; } +export function once(fn) { + let ran = false; + return function(this: any, ...args) { + if (ran) return; + ran = true; + fn.call(this, ...args); + } +} + const is_client = typeof window !== 'undefined'; export let now: () => number = is_client diff --git a/test/runtime/samples/component-event-handler-modifier-once/Button.svelte b/test/runtime/samples/component-event-handler-modifier-once/Button.svelte new file mode 100644 index 0000000000..9b5b7a5f67 --- /dev/null +++ b/test/runtime/samples/component-event-handler-modifier-once/Button.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-event-handler-modifier-once/_config.js b/test/runtime/samples/component-event-handler-modifier-once/_config.js new file mode 100644 index 0000000000..41daf374c8 --- /dev/null +++ b/test/runtime/samples/component-event-handler-modifier-once/_config.js @@ -0,0 +1,16 @@ +export default { + html: ` + + `, + + async test({ assert, component, target, window }) { + const button = target.querySelector('button'); + const event = new window.MouseEvent('click'); + + await button.dispatchEvent(event); + assert.equal(component.count, 1); + + await button.dispatchEvent(event); + assert.equal(component.count, 1); + } +}; diff --git a/test/runtime/samples/component-event-handler-modifier-once/main.svelte b/test/runtime/samples/component-event-handler-modifier-once/main.svelte new file mode 100644 index 0000000000..b9bca3b5eb --- /dev/null +++ b/test/runtime/samples/component-event-handler-modifier-once/main.svelte @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/test/validator/samples/component-event-modifiers-invalid/errors.json b/test/validator/samples/component-event-modifiers-invalid/errors.json new file mode 100644 index 0000000000..da608063fe --- /dev/null +++ b/test/validator/samples/component-event-modifiers-invalid/errors.json @@ -0,0 +1,15 @@ +[{ + "message": "Event modifiers other than 'once' can only be used on DOM elements", + "code": "invalid-event-modifier", + "start": { + "line": 6, + "column": 8, + "character": 93 + }, + "end": { + "line": 6, + "column": 40, + "character": 125 + }, + "pos": 93 +}] diff --git a/test/validator/samples/component-event-modifiers-invalid/input.svelte b/test/validator/samples/component-event-modifiers-invalid/input.svelte new file mode 100644 index 0000000000..8f7ce54d7a --- /dev/null +++ b/test/validator/samples/component-event-modifiers-invalid/input.svelte @@ -0,0 +1,6 @@ + + + \ No newline at end of file