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

@ -9,6 +9,12 @@ export default class EventHandler extends Node {
dependencies: Set<string>;
expression: Node;
callee: any; // TODO
usesComponent: boolean;
usesContext: boolean;
isCustomEvent: boolean;
shouldHoist: boolean;
insertionPoint: number;
args: Expression[];
snippet: string;
@ -22,38 +28,50 @@ export default class EventHandler extends Node {
if (info.expression) {
this.callee = flattenReference(info.expression.callee);
this.insertionPoint = info.expression.start;
this.args = info.expression.arguments.map(param => {
const expression = new Expression(compiler, this, scope, param);
addToSet(this.dependencies, expression.dependencies);
return expression;
});
this.usesComponent = !validCalleeObjects.has(this.callee.name);
this.usesContext = this.dependencies.size > 0;
this.snippet = `[✂${info.expression.start}-${info.expression.end}✂]`;
} else {
this.callee = null;
this.insertionPoint = null;
this.args = null;
this.usesComponent = true;
this.usesContext = false;
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) {
if (this.insertionPoint === null) return; // TODO handle shorthand events here?
if (!validCalleeObjects.has(this.callee.name)) {
const component = this.shouldHoist ? `component` : block.alias(`component`);
// allow event.stopPropagation(), this.select() etc
// 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)) {
compiler.code.overwrite(
this.insertionPoint,
this.insertionPoint + 1,
`${block.alias('component')}.store.`
`${component}.store.`
);
} else {
compiler.code.prependRight(
this.insertionPoint,
`${block.alias('component')}.`
`${component}.`
);
}
}

Loading…
Cancel
Save