render functions that close over contextual data - fixes #1938

pull/1942/head
Richard Harris 6 years ago
parent 45d375e15d
commit 329b013e40

@ -2,6 +2,7 @@ import Node from './shared/Node';
import Expression from './shared/Expression';
import Component from '../Component';
import deindent from '../../utils/deindent';
import Block from '../render-dom/Block';
export default class EventHandler extends Node {
name: string;
@ -52,8 +53,9 @@ export default class EventHandler extends Node {
}
}
render() {
if (this.expression) return this.expression.render();
// TODO move this? it is specific to render-dom
render(block: Block) {
if (this.expression) return this.expression.render(block);
this.component.template_references.add(this.handler_name);
return `ctx.${this.handler_name}`;

@ -11,6 +11,7 @@ import sanitize from '../../../utils/sanitize';
import TemplateScope from './TemplateScope';
import getObject from '../../../utils/getObject';
import { nodes_match } from '../../../utils/nodes_match';
import Block from '../../render-dom/Block';
const binaryOperators: Record<string, number> = {
'**': 15,
@ -86,13 +87,6 @@ export default class Expression {
Object.defineProperties(this, {
component: {
value: component
},
// TODO remove this, is just for debugging
snippet: {
get: () => {
throw new Error(`cannot access expression.snippet, use expression.render() instead`)
}
}
});
@ -181,7 +175,7 @@ export default class Expression {
}
// TODO move this into a render-dom wrapper?
render() {
render(block: Block) {
if (this.rendered) return this.rendered;
const {
@ -407,6 +401,13 @@ export default class Expression {
}
});
if (declarations.length > 0) {
block.maintainContext = true;
declarations.forEach(declaration => {
block.builders.init.addBlock(declaration);
});
}
return this.rendered = `[✂${this.node.start}-${this.node.end}✂]`;
}
}

@ -125,7 +125,7 @@ export default class AwaitBlockWrapper extends Wrapper {
const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const updateMountNode = this.getUpdateMountNode(anchor);
const snippet = this.node.expression.render();
const snippet = this.node.expression.render(block);
const info = block.getUniqueName(`info`);
const promise = block.getUniqueName(`promise`);

@ -8,7 +8,7 @@ export default class DocumentWrapper extends Wrapper {
render(block: Block, parentNode: string, parentNodes: string) {
this.node.handlers.forEach(handler => {
const snippet = handler.render();
const snippet = handler.render(block);
block.builders.init.addBlock(deindent`
document.addEventListener("${handler.name}", ${snippet});

@ -180,7 +180,7 @@ export default class EachBlockWrapper extends Wrapper {
if (this.hasBinding) this.contextProps.push(`child_ctx.${this.vars.each_block_value} = list;`);
if (this.hasBinding || this.node.index) this.contextProps.push(`child_ctx.${this.indexName} = i;`);
const snippet = this.node.expression.render();
const snippet = this.node.expression.render(block);
block.builders.init.addLine(`var ${this.vars.each_block_value} = ${snippet};`);

@ -65,7 +65,7 @@ export default class BindingWrapper {
// view to model
this.handler = getEventHandler(this, parent.renderer, block, this.object, contextless_snippet);
this.snippet = this.node.expression.render();
this.snippet = this.node.expression.render(block);
const type = parent.node.getStaticAttributeValue('type');

@ -534,7 +534,7 @@ export default class ElementWrapper extends Wrapper {
: null;
if (attr.isSpread) {
const snippet = attr.expression.render();
const snippet = attr.expression.render(block);
initialProps.push(snippet);
@ -584,7 +584,7 @@ export default class ElementWrapper extends Wrapper {
if (intro === outro) {
const name = block.getUniqueName(`${this.var}_transition`);
const snippet = intro.expression
? intro.expression.render()
? intro.expression.render(block)
: '{}';
block.addVariable(name);
@ -616,7 +616,7 @@ export default class ElementWrapper extends Wrapper {
if (intro) {
block.addVariable(introName);
const snippet = intro.expression
? intro.expression.render()
? intro.expression.render(block)
: '{}';
const fn = component.qualify(intro.name); // TODO add built-in transitions?
@ -639,7 +639,7 @@ export default class ElementWrapper extends Wrapper {
if (outro) {
block.addVariable(outroName);
const snippet = outro.expression
? outro.expression.render()
? outro.expression.render(block)
: '{}';
const fn = component.qualify(outro.name);
@ -680,7 +680,7 @@ export default class ElementWrapper extends Wrapper {
${stop_animation}();
`);
const params = this.node.animation.expression ? this.node.animation.expression.render() : '{}';
const params = this.node.animation.expression ? this.node.animation.expression.render(block) : '{}';
const name = component.qualify(this.node.animation.name);
@ -699,7 +699,7 @@ export default class ElementWrapper extends Wrapper {
const { expression, name } = classDir;
let snippet, dependencies;
if (expression) {
snippet = expression.render();
snippet = expression.render(block);
dependencies = expression.dependencies;
} else {
snippet = `${quotePropIfNecessary(name)}`;

@ -32,7 +32,7 @@ class IfBlockBranch extends Wrapper {
) {
super(renderer, block, parent, node);
this.condition = (<IfBlock>node).expression && (<IfBlock>node).expression.render();
this.condition = (<IfBlock>node).expression && (<IfBlock>node).expression.render(block);
this.block = block.child({
comment: createDebuggingComment(node, parent.renderer.component),

@ -150,7 +150,7 @@ export default class InlineComponentWrapper extends Wrapper {
: null;
if (attr.isSpread) {
const value = attr.expression.render();
const value = attr.expression.render(block);
initialProps.push(value);
changes.push(condition ? `${condition} && ${value}` : value);
@ -238,7 +238,7 @@ export default class InlineComponentWrapper extends Wrapper {
const updating = block.getUniqueName(`updating_${binding.name}`);
block.addVariable(updating);
const snippet = binding.expression.render();
const snippet = binding.expression.render(block);
statements.push(deindent`
if (${snippet} !== void 0) {
@ -305,13 +305,7 @@ export default class InlineComponentWrapper extends Wrapper {
const munged_handlers = this.node.handlers.map(handler => {
// TODO return declarations from handler.render()?
const snippet = handler.render();
if (handler.expression) {
handler.expression.declarations.forEach(declaration => {
block.builders.init.addBlock(declaration);
});
}
const snippet = handler.render(block);
return `${name}.$on("${handler.name}", ${snippet});`;
});
@ -320,7 +314,7 @@ export default class InlineComponentWrapper extends Wrapper {
const switch_value = block.getUniqueName('switch_value');
const switch_props = block.getUniqueName('switch_props');
const snippet = this.node.expression.render();
const snippet = this.node.expression.render(block);
block.builders.init.addBlock(deindent`
var ${switch_value} = ${snippet};

@ -33,7 +33,7 @@ export default class TitleWrapper extends Wrapper {
if (this.node.children.length === 1) {
// single {tag} — may be a non-string
const { expression } = this.node.children[0];
value = expression.render();
value = expression.render(block);
addToSet(allDependencies, expression.dynamic_dependencies);
} else {
// '{foo} {bar}' — treat as string concatenation
@ -44,7 +44,7 @@ export default class TitleWrapper extends Wrapper {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
const snippet = chunk.expression.render();
const snippet = chunk.expression.render(block);
chunk.expression.dynamic_dependencies.forEach(d => {
allDependencies.add(d);

@ -19,7 +19,7 @@ export default class Tag extends Wrapper {
update: ((value: string) => string)
) {
const dependencies = this.node.expression.dynamic_dependencies;
const snippet = this.node.expression.render();
const snippet = this.node.expression.render(block);
const value = this.node.shouldCache && block.getUniqueName(`${this.var}_value`);
const content = this.node.shouldCache ? value : snippet;

@ -12,13 +12,10 @@ export default function addActions(
actions.forEach(action => {
const { expression } = action;
let snippet, dependencies;
if (expression) {
snippet = expression.render();
snippet = expression.render(block);
dependencies = expression.dynamic_dependencies;
expression.declarations.forEach(declaration => {
block.builders.init.addBlock(declaration);
});
}
const name = block.getUniqueName(

@ -7,7 +7,7 @@ export default function addEventHandlers(
handlers: EventHandler[]
) {
handlers.forEach(handler => {
let snippet = handler.render();
let snippet = handler.render(block);
if (handler.modifiers.has('preventDefault')) snippet = `@preventDefault(${snippet})`;
if (handler.modifiers.has('stopPropagation')) snippet = `@stopPropagation(${snippet})`;
@ -26,11 +26,5 @@ export default function addEventHandlers(
`@addListener(${target}, "${handler.name}", ${snippet})`
);
}
if (handler.expression) {
handler.expression.declarations.forEach(declaration => {
block.builders.init.addBlock(declaration);
});
}
});
}

@ -0,0 +1,15 @@
export default {
html: `
<p>1, 2, 3</p>
<p>2, 4, 6</p>
<p>3, 6, 9</p>
`,
test({ assert, component, target }) {
component.numbers = [4, 5];
assert.htmlEqual( target.innerHTML, `
<p>16, 20</p>
<p>20, 25</p>
` );
}
};

@ -0,0 +1,7 @@
{#each numbers as i}
<p>{numbers.map(j => i * j).join(', ')}</p>
{/each}
<script>
export let numbers = [1, 2, 3];
</script>
Loading…
Cancel
Save