mirror of https://github.com/sveltejs/svelte
Fix contextual bind:this (#2806)
parent
46b2e77a7e
commit
6af23ba88c
@ -0,0 +1,80 @@
|
|||||||
|
import flatten_reference from '../../../utils/flatten_reference';
|
||||||
|
import deindent from '../../../utils/deindent';
|
||||||
|
import Component from '../../../Component';
|
||||||
|
import Block from '../../Block';
|
||||||
|
import Binding from '../../../nodes/Binding';
|
||||||
|
|
||||||
|
export default function bind_this(component: Component, block: Block, binding: Binding, variable: string) {
|
||||||
|
const fn = component.get_unique_name(`${variable}_binding`);
|
||||||
|
|
||||||
|
component.add_var({
|
||||||
|
name: fn,
|
||||||
|
internal: true,
|
||||||
|
referenced: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let lhs;
|
||||||
|
let object;
|
||||||
|
|
||||||
|
if (binding.is_contextual && binding.expression.node.type === 'Identifier') {
|
||||||
|
// bind:x={y} — we can't just do `y = x`, we need to
|
||||||
|
// to `array[index] = x;
|
||||||
|
const { name } = binding.expression.node;
|
||||||
|
const { snippet } = block.bindings.get(name);
|
||||||
|
lhs = snippet;
|
||||||
|
|
||||||
|
// TODO we need to invalidate... something
|
||||||
|
} else {
|
||||||
|
object = flatten_reference(binding.expression.node).name;
|
||||||
|
lhs = component.source.slice(binding.expression.node.start, binding.expression.node.end).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextual_dependencies = Array.from(binding.expression.contextual_dependencies);
|
||||||
|
|
||||||
|
if (contextual_dependencies.length) {
|
||||||
|
component.partly_hoisted.push(deindent`
|
||||||
|
function ${fn}(${['$$value', ...contextual_dependencies].join(', ')}) {
|
||||||
|
if (${lhs} === $$value) return;
|
||||||
|
${lhs} = $$value;
|
||||||
|
${object && component.invalidate(object)}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const args = [];
|
||||||
|
for (const arg of contextual_dependencies) {
|
||||||
|
args.push(arg);
|
||||||
|
block.add_variable(arg, `ctx.${arg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const assign = block.get_unique_name(`assign_${variable}`);
|
||||||
|
const unassign = block.get_unique_name(`unassign_${variable}`);
|
||||||
|
|
||||||
|
block.builders.init.add_block(deindent`
|
||||||
|
const ${assign} = () => ctx.${fn}(${[variable].concat(args).join(', ')});
|
||||||
|
const ${unassign} = () => ctx.${fn}(${['null'].concat(args).join(', ')});
|
||||||
|
`);
|
||||||
|
|
||||||
|
const condition = Array.from(contextual_dependencies).map(name => `${name} !== ctx.${name}`).join(' || ');
|
||||||
|
|
||||||
|
block.builders.update.add_line(deindent`
|
||||||
|
if (${condition}) {
|
||||||
|
${unassign}();
|
||||||
|
${args.map(a => `${a} = ctx.${a}`).join(', ')};
|
||||||
|
@add_binding_callback(${assign});
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
block.builders.destroy.add_line(`${unassign}();`);
|
||||||
|
return `@add_binding_callback(${assign});`;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.partly_hoisted.push(deindent`
|
||||||
|
function ${fn}($$value) {
|
||||||
|
${lhs} = $$value;
|
||||||
|
${object && component.invalidate(object)}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
block.builders.destroy.add_line(`ctx.${fn}(null);`);
|
||||||
|
return `@add_binding_callback(() => ctx.${fn}(${variable}));`;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
export function isFoo() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p><slot></slot></p>
|
@ -0,0 +1,12 @@
|
|||||||
|
export default {
|
||||||
|
html: ``,
|
||||||
|
|
||||||
|
async test({ assert, component, target }) {
|
||||||
|
component.visible = true;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>a</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
assert.ok(component.items[0].ref.isFoo());
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
<script>
|
||||||
|
import Foo from './Foo.svelte';
|
||||||
|
|
||||||
|
export let visible = false;
|
||||||
|
|
||||||
|
export let items = [{ value: 'a', ref: null }];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if visible}
|
||||||
|
{#each items as item}
|
||||||
|
<Foo bind:this={item.ref}>{item.value}</Foo>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
@ -0,0 +1,12 @@
|
|||||||
|
export default {
|
||||||
|
html: ``,
|
||||||
|
|
||||||
|
async test({ assert, component, target }) {
|
||||||
|
component.visible = true;
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<div>a</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
assert.equal(component.items[0].ref, target.querySelector('div'));
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,11 @@
|
|||||||
|
<script>
|
||||||
|
export let visible = false;
|
||||||
|
|
||||||
|
export let items = [{ value: 'a', ref: null }];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if visible}
|
||||||
|
{#each items as item}
|
||||||
|
<div bind:this={item.ref}>{item.value}</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
Loading…
Reference in new issue