fix contextual events

pull/1367/head
Rich Harris 7 years ago
parent 6b3d7c2cf5
commit c62178db3e

@ -300,49 +300,49 @@ export default class Element extends Node {
}); });
} }
this.addBindings(block, allUsedContexts); let hasHoistedEventHandlerOrBinding = (
const eventHandlerUsesComponent = this.addEventHandlers(block, allUsedContexts); (this.hasAncestor('EachBlock') && this.bindings.length > 0) ||
if (this.ref) this.addRef(block); this.handlers.some(handler => handler.shouldHoist)
this.addAttributes(block); );
this.addTransitions(block); let eventHandlerOrBindingUsesComponent;
this.addActions(block); let eventHandlerOrBindingUsesContext;
if (allUsedContexts.size || eventHandlerUsesComponent) { if (this.bindings.length > 0) {
eventHandlerOrBindingUsesComponent = true;
eventHandlerOrBindingUsesContext = true;
} else {
eventHandlerOrBindingUsesComponent = this.handlers.some(handler => handler.usesComponent);
eventHandlerOrBindingUsesContext = this.handlers.some(handler => handler.usesContext);
}
if (hasHoistedEventHandlerOrBinding) {
const initialProps: string[] = []; const initialProps: string[] = [];
const updates: string[] = []; const updates: string[] = [];
if (eventHandlerUsesComponent) { if (eventHandlerOrBindingUsesComponent) {
initialProps.push(`component: #component`); const component = block.alias('component');
initialProps.push(component === 'component' ? 'component' : `component: ${component}`);
} }
allUsedContexts.forEach((contextName: string) => { if (eventHandlerOrBindingUsesContext) {
if (contextName === 'state') return; initialProps.push(`ctx`);
if (block.contextTypes.get(contextName) !== 'each') return; block.builders.update.addLine(`${name}._svelte.ctx = ctx;`);
}
const listName = block.listNames.get(contextName);
const indexName = block.indexNames.get(contextName);
initialProps.push(
`${listName}: ctx.${listName},\n${indexName}: ctx.${indexName}`
);
updates.push(
`${name}._svelte.${listName} = ctx.${listName};\n${name}._svelte.${indexName} = ctx.${indexName};`
);
});
if (initialProps.length) { if (initialProps.length) {
block.builders.hydrate.addBlock(deindent` block.builders.hydrate.addBlock(deindent`
${name}._svelte = { ${name}._svelte = { ${initialProps.join(', ')} };
${initialProps.join(',\n')}
};
`); `);
} }
if (updates.length) {
block.builders.update.addBlock(updates.join('\n'));
}
} }
this.addBindings(block, allUsedContexts);
this.addEventHandlers(block, allUsedContexts);
if (this.ref) this.addRef(block);
this.addAttributes(block);
this.addTransitions(block);
this.addActions(block);
if (this.initialUpdate) { if (this.initialUpdate) {
block.builders.mount.addBlock(this.initialUpdate); block.builders.mount.addBlock(this.initialUpdate);
} }
@ -557,33 +557,18 @@ export default class Element extends Node {
addEventHandlers(block: Block, allUsedContexts) { addEventHandlers(block: Block, allUsedContexts) {
const { compiler } = this; const { compiler } = this;
let eventHandlerUsesComponent = false;
this.handlers.forEach(handler => { this.handlers.forEach(handler => {
const isCustomEvent = compiler.events.has(handler.name); const isCustomEvent = compiler.events.has(handler.name);
const shouldHoist = !isCustomEvent && this.hasAncestor('EachBlock'); const shouldHoist = !isCustomEvent && this.hasAncestor('EachBlock');
const context = shouldHoist ? null : this.var; const context = shouldHoist ? null : this.var;
const usedContexts: string[] = [];
if (handler.callee) { if (handler.callee) {
handler.render(this.compiler, block); handler.render(this.compiler, block);
if (!validCalleeObjects.has(handler.callee.name)) {
if (shouldHoist) eventHandlerUsesComponent = true; // this feels a bit hacky but it works!
} }
// handler.expression.arguments.forEach((arg: Node) => { const target = context || 'this';
// const { contexts } = block.contextualise(arg, context, true);
// contexts.forEach(context => {
// if (!~usedContexts.indexOf(context)) usedContexts.push(context);
// allUsedContexts.add(context);
// });
// });
}
const ctx = context || 'this';
// get a name for the event handler that is globally unique // get a name for the event handler that is globally unique
// if hoisted, locally unique otherwise // if hoisted, locally unique otherwise
@ -595,9 +580,12 @@ export default class Element extends Node {
// create the handler body // create the handler body
const handlerBody = deindent` const handlerBody = deindent`
${eventHandlerUsesComponent && ${handler.shouldHoist && (
`var ${component} = ${ctx}._svelte.component;`} handler.usesComponent || handler.usesContext
${handler.dependencies.size > 0 && `const ctx = ${component}.get();`} ? `const { ${[handler.usesComponent && 'component', handler.usesContext && 'ctx'].filter(Boolean).join(', ')} } = ${target}._svelte;`
: null
)}
${handler.snippet ? ${handler.snippet ?
handler.snippet : handler.snippet :
`${component}.fire("${handler.name}", event);`} `${component}.fire("${handler.name}", event);`}
@ -637,7 +625,6 @@ export default class Element extends Node {
); );
} }
}); });
return eventHandlerUsesComponent;
} }
addRef(block: Block) { addRef(block: Block) {

@ -9,6 +9,12 @@ export default class EventHandler extends Node {
dependencies: Set<string>; dependencies: Set<string>;
expression: Node; expression: Node;
callee: any; // TODO callee: any; // TODO
usesComponent: boolean;
usesContext: boolean;
isCustomEvent: boolean;
shouldHoist: boolean;
insertionPoint: number; insertionPoint: number;
args: Expression[]; args: Expression[];
snippet: string; snippet: string;
@ -22,38 +28,50 @@ export default class EventHandler extends Node {
if (info.expression) { if (info.expression) {
this.callee = flattenReference(info.expression.callee); this.callee = flattenReference(info.expression.callee);
this.insertionPoint = info.expression.start; this.insertionPoint = info.expression.start;
this.args = info.expression.arguments.map(param => { this.args = info.expression.arguments.map(param => {
const expression = new Expression(compiler, this, scope, param); const expression = new Expression(compiler, this, scope, param);
addToSet(this.dependencies, expression.dependencies); addToSet(this.dependencies, expression.dependencies);
return expression; return expression;
}); });
this.usesComponent = !validCalleeObjects.has(this.callee.name);
this.usesContext = this.dependencies.size > 0;
this.snippet = `[✂${info.expression.start}-${info.expression.end}✂]`; this.snippet = `[✂${info.expression.start}-${info.expression.end}✂]`;
} else { } else {
this.callee = null; this.callee = null;
this.insertionPoint = null; this.insertionPoint = null;
this.args = null; this.args = null;
this.usesComponent = true;
this.usesContext = false;
this.snippet = null; // TODO handle shorthand events here? this.snippet = null; // TODO handle shorthand events here?
} }
this.isCustomEvent = compiler.events.has(this.name);
this.shouldHoist = !this.isCustomEvent && parent.hasAncestor('EachBlock');
} }
render(compiler, block) { render(compiler, block) {
if (this.insertionPoint === null) return; // TODO handle shorthand events here? if (this.insertionPoint === null) return; // TODO handle shorthand events here?
if (!validCalleeObjects.has(this.callee.name)) { if (!validCalleeObjects.has(this.callee.name)) {
const component = this.shouldHoist ? `component` : block.alias(`component`);
// allow event.stopPropagation(), this.select() etc // allow event.stopPropagation(), this.select() etc
// TODO verify that it's a valid callee (i.e. built-in or declared method) // TODO verify that it's a valid callee (i.e. built-in or declared method)
if (this.callee.name[0] === '$' && !compiler.methods.has(this.callee.name)) { if (this.callee.name[0] === '$' && !compiler.methods.has(this.callee.name)) {
compiler.code.overwrite( compiler.code.overwrite(
this.insertionPoint, this.insertionPoint,
this.insertionPoint + 1, this.insertionPoint + 1,
`${block.alias('component')}.store.` `${component}.store.`
); );
} else { } else {
compiler.code.prependRight( compiler.code.prependRight(
this.insertionPoint, this.insertionPoint,
`${block.alias('component')}.` `${component}.`
); );
} }
} }

Loading…
Cancel
Save