diff --git a/src/compile/Component.ts b/src/compile/Component.ts index 7d4aa72f0318..1d142c7d780a 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -753,18 +753,34 @@ export default class Component { }); } - invalidate(name, value = name) { + invalidate(name, value) { const variable = this.var_lookup.get(name); if (variable && (variable.subscribable && variable.reassigned)) { - return `$$subscribe_${name}(), $$invalidate('${name}', ${value})`; + return `$$subscribe_${name}(), $$invalidate('${name}', ${value || name})`; } if (name[0] === '$' && name[1] !== '$') { return `${name.slice(1)}.set(${name})` } - return `$$invalidate('${name}', ${value})`; + if (value) { + return `$$invalidate('${name}', ${value})`; + } + + // if this is a reactive declaration, invalidate dependencies recursively + const deps = new Set([name]); + + deps.forEach(name => { + const reactive_declarations = this.reactive_declarations.filter(x => x.assignees.has(name)); + reactive_declarations.forEach(declaration => { + declaration.dependencies.forEach(name => { + deps.add(name); + }); + }); + }); + + return Array.from(deps).map(n => `$$invalidate('${n}', ${n})`).join(', '); } rewrite_props(get_insert: (variable: Var) => string) { diff --git a/test/runtime/samples/binding-input-text-contextual-reactive/_config.js b/test/runtime/samples/binding-input-text-contextual-reactive/_config.js new file mode 100644 index 000000000000..6fdfc4be527f --- /dev/null +++ b/test/runtime/samples/binding-input-text-contextual-reactive/_config.js @@ -0,0 +1,125 @@ +export default { + props: { + items: [ + { done: false, text: 'one' }, + { done: true, text: 'two' }, + { done: false, text: 'three' } + ] + }, + + html: ` +
one
+two
+three
+remaining:one / done:two / remaining:three
+ `, + + ssrHtml: ` +one
+two
+three
+remaining:one / done:two / remaining:three
+ `, + + async test({ assert, component, target, window }) { + function set_text(i, text) { + const input = target.querySelectorAll('input[type="text"]')[i]; + input.value = text; + input.dispatchEvent(new window.Event('input')); + } + + function set_done(i, done) { + const input = target.querySelectorAll('input[type="checkbox"]')[i]; + input.checked = done; + input.dispatchEvent(new window.Event('change')); + } + + component.filter = 'remaining'; + + assert.htmlEqual(target.innerHTML, ` +one
+three
+remaining:one / done:two / remaining:three
+ `); + + await set_text(1, 'four'); + + assert.htmlEqual(target.innerHTML, ` +one
+four
+remaining:one / done:two / remaining:four
+ `); + + assert.deepEqual(component.items, [ + { done: false, text: 'one' }, + { done: true, text: 'two' }, + { done: false, text: 'four' } + ]); + + await set_done(0, true); + + assert.htmlEqual(target.innerHTML, ` +four
+done:one / done:two / remaining:four
+ `); + + assert.deepEqual(component.items, [ + { done: true, text: 'one' }, + { done: true, text: 'two' }, + { done: false, text: 'four' } + ]); + + component.filter = 'done'; + + assert.htmlEqual(target.innerHTML, ` +one
+two
+done:one / done:two / remaining:four
+ `); + }, +}; diff --git a/test/runtime/samples/binding-input-text-contextual-reactive/main.svelte b/test/runtime/samples/binding-input-text-contextual-reactive/main.svelte new file mode 100644 index 000000000000..7e1019953036 --- /dev/null +++ b/test/runtime/samples/binding-input-text-contextual-reactive/main.svelte @@ -0,0 +1,25 @@ + + +{#each filtered as item} +{item.text}
+{summary}
\ No newline at end of file