diff --git a/src/generators/dom/visitors/Component/EventHandler.ts b/src/generators/dom/visitors/Component/EventHandler.ts
index 1010456253a9..0abef438213c 100644
--- a/src/generators/dom/visitors/Component/EventHandler.ts
+++ b/src/generators/dom/visitors/Component/EventHandler.ts
@@ -13,22 +13,25 @@ export default function visitEventHandler(
local
) {
// TODO verify that it's a valid callee (i.e. built-in or declared method)
- generator.addSourcemapLocations(attribute.expression);
- generator.code.prependRight(
- attribute.expression.start,
- `${block.alias('component')}.`
- );
-
const usedContexts: string[] = [];
- attribute.expression.arguments.forEach((arg: Node) => {
- const { contexts } = block.contextualise(arg, null, true);
- contexts.forEach(context => {
- if (!~usedContexts.indexOf(context)) usedContexts.push(context);
- if (!~local.allUsedContexts.indexOf(context))
- local.allUsedContexts.push(context);
+ if (attribute.expression) {
+ generator.addSourcemapLocations(attribute.expression);
+ generator.code.prependRight(
+ attribute.expression.start,
+ `${block.alias('component')}.`
+ );
+
+ attribute.expression.arguments.forEach((arg: Node) => {
+ const { contexts } = block.contextualise(arg, null, true);
+
+ contexts.forEach(context => {
+ if (!~usedContexts.indexOf(context)) usedContexts.push(context);
+ if (!~local.allUsedContexts.indexOf(context))
+ local.allUsedContexts.push(context);
+ });
});
- });
+ }
// TODO hoist event handlers? can do `this.__component.method(...)`
const declarations = usedContexts.map(name => {
@@ -42,7 +45,9 @@ export default function visitEventHandler(
const handlerBody =
(declarations.length ? declarations.join('\n') + '\n\n' : '') +
- `[✂${attribute.expression.start}-${attribute.expression.end}✂];`;
+ (attribute.expression ?
+ `[✂${attribute.expression.start}-${attribute.expression.end}✂];` :
+ `${block.alias('component')}.fire('${attribute.name}', event);`);
local.create.addBlock(deindent`
${local.name}.on( '${attribute.name}', function ( event ) {
diff --git a/src/generators/dom/visitors/Element/EventHandler.ts b/src/generators/dom/visitors/Element/EventHandler.ts
index eb96c4e63674..56fdb42a8710 100644
--- a/src/generators/dom/visitors/Element/EventHandler.ts
+++ b/src/generators/dom/visitors/Element/EventHandler.ts
@@ -16,30 +16,33 @@ export default function visitEventHandler(
const isCustomEvent = generator.events.has(name);
const shouldHoist = !isCustomEvent && state.inEachBlock;
- generator.addSourcemapLocations(attribute.expression);
-
- const flattened = flattenReference(attribute.expression.callee);
- if (flattened.name !== 'event' && flattened.name !== 'this') {
- // allow event.stopPropagation(), this.select() etc
- // TODO verify that it's a valid callee (i.e. built-in or declared method)
- generator.code.prependRight(
- attribute.expression.start,
- `${block.alias('component')}.`
- );
- if (shouldHoist) state.usesComponent = true; // this feels a bit hacky but it works!
- }
-
const context = shouldHoist ? null : state.parentNode;
const usedContexts: string[] = [];
- attribute.expression.arguments.forEach((arg: Node) => {
- const { contexts } = block.contextualise(arg, context, true);
- contexts.forEach(context => {
- if (!~usedContexts.indexOf(context)) usedContexts.push(context);
- if (!~state.allUsedContexts.indexOf(context))
- state.allUsedContexts.push(context);
+ if (attribute.expression) {
+ generator.addSourcemapLocations(attribute.expression);
+
+ const flattened = flattenReference(attribute.expression.callee);
+ if (flattened.name !== 'event' && flattened.name !== 'this') {
+ // allow event.stopPropagation(), this.select() etc
+ // TODO verify that it's a valid callee (i.e. built-in or declared method)
+ generator.code.prependRight(
+ attribute.expression.start,
+ `${block.alias('component')}.`
+ );
+ if (shouldHoist) state.usesComponent = true; // this feels a bit hacky but it works!
+ }
+
+ attribute.expression.arguments.forEach((arg: Node) => {
+ const { contexts } = block.contextualise(arg, context, true);
+
+ contexts.forEach(context => {
+ if (!~usedContexts.indexOf(context)) usedContexts.push(context);
+ if (!~state.allUsedContexts.indexOf(context))
+ state.allUsedContexts.push(context);
+ });
});
- });
+ }
const _this = context || 'this';
const declarations = usedContexts.map(name => {
@@ -66,7 +69,9 @@ export default function visitEventHandler(
${state.usesComponent &&
`var ${block.alias('component')} = ${_this}._svelte.component;`}
${declarations}
- [✂${attribute.expression.start}-${attribute.expression.end}✂];
+ ${attribute.expression ?
+ `[✂${attribute.expression.start}-${attribute.expression.end}✂];` :
+ `${block.alias('component')}.fire('${attribute.name}', event);`}
`;
if (isCustomEvent) {
diff --git a/src/parse/index.ts b/src/parse/index.ts
index 514e074fd4ae..b9748515e5a9 100644
--- a/src/parse/index.ts
+++ b/src/parse/index.ts
@@ -111,6 +111,8 @@ export class Parser {
if (required) {
this.error(`Expected ${str}`);
}
+
+ return false;
}
match(str: string) {
diff --git a/src/parse/read/directives.ts b/src/parse/read/directives.ts
index 6064a61fcbce..0b61ad635025 100644
--- a/src/parse/read/directives.ts
+++ b/src/parse/read/directives.ts
@@ -43,16 +43,21 @@ function readExpression(parser: Parser, start: number, quoteMark) {
export function readEventHandlerDirective(
parser: Parser,
start: number,
- name: string
+ name: string,
+ hasValue: boolean
) {
- const quoteMark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
+ let expression;
- const expressionStart = parser.index;
+ if (hasValue) {
+ const quoteMark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null;
- const expression = readExpression(parser, expressionStart, quoteMark);
+ const expressionStart = parser.index;
- if (expression.type !== 'CallExpression') {
- parser.error(`Expected call expression`, expressionStart);
+ expression = readExpression(parser, expressionStart, quoteMark);
+
+ if (expression.type !== 'CallExpression') {
+ parser.error(`Expected call expression`, expressionStart);
+ }
}
return {
diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts
index 6fee187dd926..cfa056136578 100644
--- a/src/parse/state/tag.ts
+++ b/src/parse/state/tag.ts
@@ -256,8 +256,7 @@ function readAttribute(parser: Parser, uniqueNames) {
parser.allowWhitespace();
if (/^on:/.test(name)) {
- parser.eat('=', true);
- return readEventHandlerDirective(parser, start, name.slice(3));
+ return readEventHandlerDirective(parser, start, name.slice(3), parser.eat('='));
}
if (/^bind:/.test(name)) {
diff --git a/src/validate/html/validateEventHandler.ts b/src/validate/html/validateEventHandler.ts
index 85fdad08e656..ec4476d3fbd6 100644
--- a/src/validate/html/validateEventHandler.ts
+++ b/src/validate/html/validateEventHandler.ts
@@ -9,6 +9,8 @@ export default function validateEventHandlerCallee(
validator: Validator,
attribute: Node
) {
+ if (!attribute.expression) return;
+
const { callee, start, type } = attribute.expression;
if (type !== 'CallExpression') {
diff --git a/test/runtime/samples/event-handler-shorthand-component/Widget.html b/test/runtime/samples/event-handler-shorthand-component/Widget.html
new file mode 100644
index 000000000000..47e1f95a2f93
--- /dev/null
+++ b/test/runtime/samples/event-handler-shorthand-component/Widget.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/runtime/samples/event-handler-shorthand-component/_config.js b/test/runtime/samples/event-handler-shorthand-component/_config.js
new file mode 100644
index 000000000000..e0e21f1400df
--- /dev/null
+++ b/test/runtime/samples/event-handler-shorthand-component/_config.js
@@ -0,0 +1,18 @@
+export default {
+ html: `
+
+ `,
+
+ test (assert, component, target, window) {
+ const button = target.querySelector('button');
+ const event = new window.MouseEvent('click');
+
+ let answer;
+ component.on('foo', event => {
+ answer = event.answer;
+ });
+
+ button.dispatchEvent(event);
+ assert.equal(answer, 42);
+ }
+};
diff --git a/test/runtime/samples/event-handler-shorthand-component/main.html b/test/runtime/samples/event-handler-shorthand-component/main.html
new file mode 100644
index 000000000000..5eb9bbb3eb5c
--- /dev/null
+++ b/test/runtime/samples/event-handler-shorthand-component/main.html
@@ -0,0 +1,11 @@
+
+
+
\ No newline at end of file
diff --git a/test/runtime/samples/event-handler-shorthand/_config.js b/test/runtime/samples/event-handler-shorthand/_config.js
new file mode 100644
index 000000000000..ceded0bf0809
--- /dev/null
+++ b/test/runtime/samples/event-handler-shorthand/_config.js
@@ -0,0 +1,13 @@
+export default {
+ html: `
+
+ `,
+
+ test (assert, component, target, window) {
+ const button = target.querySelector('button');
+ const event = new window.MouseEvent('click');
+
+ button.dispatchEvent(event);
+ assert.ok(component.clicked);
+ }
+};
diff --git a/test/runtime/samples/event-handler-shorthand/main.html b/test/runtime/samples/event-handler-shorthand/main.html
new file mode 100644
index 000000000000..5b283cbb5998
--- /dev/null
+++ b/test/runtime/samples/event-handler-shorthand/main.html
@@ -0,0 +1,11 @@
+
+
+
\ No newline at end of file