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