diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index a9dbd4dbe2..a725953ffa 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -61,12 +61,16 @@ export default function visitComponent( let beforecreate: string = null; const attributes = node.attributes - .filter((a: Node) => a.type === 'Attribute') - .map((a: Node) => mungeAttribute(a, block)); + .filter(a => a.type === 'Attribute') + .map(a => mungeAttribute(a, block)); const bindings = node.attributes - .filter((a: Node) => a.type === 'Binding') - .map((a: Node) => mungeBinding(a, block)); + .filter(a => a.type === 'Binding') + .map(a => mungeBinding(a, block)); + + const eventHandlers = node.attributes + .filter((a: Node) => a.type === 'EventHandler') + .map(a => mungeEventHandler(generator, node, a, block, name_context, allContexts)); const ref = node.attributes.find((a: Node) => a.type === 'Ref'); if (ref) generator.usesRefs = true; @@ -244,9 +248,7 @@ export default function visitComponent( block.builders.init.addBlock(deindent` var ${switch_vars.value} = ${snippet}; - `); - block.builders.init.addBlock(deindent` function ${switch_vars.props}(${params}) { return { ${componentInitProperties.join(',\n')} @@ -259,6 +261,14 @@ export default function visitComponent( ${beforecreate} } + + ${eventHandlers.map(handler => deindent` + function ${handler.var}(event) { + ${handler.body} + } + + if (${name}) ${name}.on("${handler.name}", ${handler.var}); + `)} `); block.builders.create.addLine( @@ -281,6 +291,11 @@ export default function visitComponent( ${name} = new ${switch_vars.value}(${switch_vars.props}(${params})); ${name}._fragment.c(); ${name}._mount(${anchor}.parentNode, ${anchor}); + + ${eventHandlers.map(handler => deindent` + ${name}.on("${handler.name}", ${handler.var}); + `)} + ${ref && `#component.refs.${ref.name} = ${name};`} } @@ -305,6 +320,12 @@ export default function visitComponent( ${beforecreate} + ${eventHandlers.map(handler => deindent` + ${name}.on("${handler.name}", function(event) { + ${handler.body} + }); + `)} + ${ref && `#component.refs.${ref.name} = ${name};`} `); @@ -326,50 +347,6 @@ export default function visitComponent( `); } - // event handlers - node.attributes.filter((a: Node) => a.type === 'EventHandler').forEach((handler: Node) => { - const usedContexts: string[] = []; - - if (handler.expression) { - generator.addSourcemapLocations(handler.expression); - generator.code.prependRight( - handler.expression.start, - `${block.alias('component')}.` - ); - - handler.expression.arguments.forEach((arg: Node) => { - const { contexts } = block.contextualise(arg, null, true); - - contexts.forEach(context => { - if (!~usedContexts.indexOf(context)) usedContexts.push(context); - allContexts.add(context); - }); - }); - } - - // TODO hoist event handlers? can do `this.__component.method(...)` - const declarations = usedContexts.map(name => { - if (name === 'state') return `var state = ${name_context}.state;`; - - const listName = block.listNames.get(name); - const indexName = block.indexNames.get(name); - - return `var ${listName} = ${name_context}.${listName}, ${indexName} = ${name_context}.${indexName}, ${name} = ${listName}[${indexName}]`; - }); - - const handlerBody = - (declarations.length ? declarations.join('\n') + '\n\n' : '') + - (handler.expression ? - `[✂${handler.expression.start}-${handler.expression.end}✂];` : - `${block.alias('component')}.fire('${handler.name}', event);`); - - block.builders.init.addBlock(deindent` - ${name}.on("${handler.name}", function(event) { - ${handlerBody} - }); - `); - }); - // maintain component context if (allContexts.size) { const contexts = Array.from(allContexts); @@ -512,6 +489,55 @@ function mungeBinding(binding: Node, block: Block): Binding { }; } +function mungeEventHandler(generator: DomGenerator, node: Node, handler: Node, block: Block, name_context: string, allContexts: Set) { + let body; + + if (handler.expression) { + generator.addSourcemapLocations(handler.expression); + generator.code.prependRight( + handler.expression.start, + `${block.alias('component')}.` + ); + + const usedContexts: string[] = []; + + handler.expression.arguments.forEach((arg: Node) => { + const { contexts } = block.contextualise(arg, null, true); + + contexts.forEach(context => { + if (!~usedContexts.indexOf(context)) usedContexts.push(context); + allContexts.add(context); + }); + }); + + // TODO hoist event handlers? can do `this.__component.method(...)` + const declarations = usedContexts.map(name => { + if (name === 'state') return `var state = ${name_context}.state;`; + + const listName = block.listNames.get(name); + const indexName = block.indexNames.get(name); + + return `var ${listName} = ${name_context}.${listName}, ${indexName} = ${name_context}.${indexName}, ${name} = ${listName}[${indexName}]`; + }); + + body = deindent` + ${declarations} + + [✂${handler.expression.start}-${handler.expression.end}✂]; + `; + } else { + body = deindent` + ${block.alias('component')}.fire('${handler.name}', event); + `; + } + + return { + name: handler.name, + var: block.getUniqueName(`${node.var}_${handler.name}`), + body + }; +} + function isComputed(node: Node) { while (node.type === 'MemberExpression') { if (node.computed) return true; diff --git a/test/runtime/samples/switch-events/Bar.html b/test/runtime/samples/switch-events/Bar.html new file mode 100644 index 0000000000..e6f79984c4 --- /dev/null +++ b/test/runtime/samples/switch-events/Bar.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/switch-events/Foo.html b/test/runtime/samples/switch-events/Foo.html new file mode 100644 index 0000000000..3601714a55 --- /dev/null +++ b/test/runtime/samples/switch-events/Foo.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/switch-events/_config.js b/test/runtime/samples/switch-events/_config.js new file mode 100644 index 0000000000..3315382e91 --- /dev/null +++ b/test/runtime/samples/switch-events/_config.js @@ -0,0 +1,27 @@ +export default { + data: { + x: true + }, + + html: ` + + `, + + test(assert, component, target, window) { + const click = new window.MouseEvent('click'); + + target.querySelector('button').dispatchEvent(click); + assert.equal(component.get('selected'), 'foo'); + + component.set({ + x: false + }); + + assert.htmlEqual(target.innerHTML, ` + + `); + + target.querySelector('button').dispatchEvent(click); + assert.equal(component.get('selected'), 'bar'); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/switch-events/main.html b/test/runtime/samples/switch-events/main.html new file mode 100644 index 0000000000..2cfd9d3ce6 --- /dev/null +++ b/test/runtime/samples/switch-events/main.html @@ -0,0 +1,12 @@ +<:Switch { x ? Foo : Bar } on:select='set({ selected: event.id })'/> + + \ No newline at end of file