fix: correctly assign bind:this with multiples

pull/9617/head
Dominic Gannaway 3 years ago
parent 8118efd115
commit 99652d6199

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: correct bind this multiple bindings

@ -891,6 +891,7 @@ function serialize_inline_component(node, component_name, context) {
if (bind_this !== null) {
const prev = fn;
const assignment = b.assignment('=', bind_this, b.id('$$value'));
const bind_this_id = bind_this;
fn = (node_id) =>
b.call(
'$.bind_this',
@ -898,7 +899,8 @@ function serialize_inline_component(node, component_name, context) {
b.arrow(
[b.id('$$value')],
serialize_set_binding(assignment, context, () => context.visit(assignment))
)
),
bind_this_id
);
}
@ -2620,7 +2622,7 @@ export const template_visitors = {
}
case 'this':
call_expr = b.call(`$.bind_this`, state.node, setter);
call_expr = b.call(`$.bind_this`, state.node, setter, node.expression);
break;
case 'textContent':

@ -79,11 +79,18 @@ const all_registerd_events = new Set();
/** @type {Set<(events: Array<string>) => void>} */
const root_event_handles = new Set();
/** @type {any[] | null} */
export let bind_this_context = null;
/** @returns {Text} */
export function empty() {
return document.createTextNode('');
}
export function clear_bind_this_context() {
bind_this_context = null;
}
/**
* @param {string} html
* @param {boolean} is_fragment
@ -1225,14 +1232,26 @@ export function bind_prop(props, prop, value) {
/**
* @param {Element} element_or_component
* @param {(value: unknown) => void} update
* @param {import('./types.js').MaybeSignal} binding
* @returns {void}
*/
export function bind_this(element_or_component, update) {
export function bind_this(element_or_component, update, binding) {
untrack(() => {
// If a bind_this is shared across multple targets, then we
// need to ensure we correctly assign the value rather than
// only setting `null` for when they unmount.
if (is_signal(binding)) {
if (bind_this_context === null) {
bind_this_context = [];
}
bind_this_context.push(binding);
}
update(element_or_component);
render_effect(() => () => {
untrack(() => {
update(null);
if (bind_this_context === null || !bind_this_context.includes(binding)) {
update(null);
}
});
});
});

@ -1,7 +1,7 @@
import { DEV } from 'esm-env';
import { subscribe_to_store } from '../../store/utils.js';
import { EMPTY_FUNC, run_all } from '../common.js';
import { unwrap } from './render.js';
import { bind_this_context, clear_bind_this_context, unwrap } from './render.js';
import { is_array } from './utils.js';
export const SOURCE = 1;
@ -478,6 +478,9 @@ function flush_queued_effects(effects) {
}
}
}
if (bind_this_context !== null) {
clear_bind_this_context();
}
effects.length = 0;
}
}

@ -0,0 +1,25 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
test({ assert, component, target }) {
const [b1, b2, b3] = target.querySelectorAll('button');
const first_h1 = target.querySelector('h1');
assert.deepEqual(component.log, [undefined, first_h1]);
flushSync(() => {
b3.click();
});
const third_h1 = target.querySelector('h1');
assert.deepEqual(component.log, [undefined, first_h1, third_h1]);
flushSync(() => {
b1.click();
});
assert.deepEqual(component.log, [undefined, first_h1, third_h1, target.querySelector('h1')]);
}
});

@ -0,0 +1,27 @@
<script>
let activeTab = 0;
let activeHeading;
export let log = [];
$: log.push(activeHeading);
</script>
<div class="tabs">
<div class="tab-toggles">
<button class:active={activeTab === 0} on:click={() => activeTab = 0}>Tab 1</button>
<button class:active={activeTab === 1} on:click={() => activeTab = 1}>Tab 2</button>
<button class:active={activeTab === 2} on:click={() => activeTab = 2}>Tab 3</button>
</div>
<div class="tab-content">
{#if activeTab === 0}
<div><h1 bind:this={activeHeading}>Tab 1</h1></div>
{/if}
{#if activeTab === 1}
<div><h1 bind:this={activeHeading}>Tab 2</h1></div>
{/if}
{#if activeTab === 2}
<div><h1 bind:this={activeHeading}>Tab 3</h1></div>
{/if}
</div>
<duiv>
</div>
Loading…
Cancel
Save