Skip to content

Commit 27e37d1

Browse files
committed
fix: replay function invocations on custom element
fixes #8954
1 parent 258ccf2 commit 27e37d1

File tree

5 files changed

+56
-2
lines changed

5 files changed

+56
-2
lines changed

.changeset/thick-yaks-eat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: replay function invocations on custom element

packages/svelte/src/runtime/internal/Component.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ if (typeof HTMLElement === 'function') {
169169
$$cn = false;
170170
/** Component props data */
171171
$$d = {};
172+
/** Component binding invocations recorded before the component was mounted, to playback on creation */
173+
$$b = [];
172174
/** `true` if currently in the process of reflecting component props back to attributes */
173175
$$r = false;
174176
/** @type {Record<string, CustomElementPropDefinition>} Props definition (name, reflected, type etc) */
@@ -270,6 +272,11 @@ if (typeof HTMLElement === 'function') {
270272
}
271273
});
272274

275+
// Replay binding invocations
276+
for (const binding of this.$$b) {
277+
binding();
278+
}
279+
273280
// Reflect component props as attributes
274281
const reflect_attributes = () => {
275282
this.$$r = true;
@@ -410,14 +417,25 @@ export function create_custom_element(
410417
set(value) {
411418
value = get_custom_element_value(prop, value, props_definition);
412419
this.$$d[prop] = value;
420+
console.log('did set', value, this.$$d);
413421
this.$$c?.$set({ [prop]: value });
414422
}
415423
});
416424
});
417425
accessors.forEach((accessor) => {
418426
Object.defineProperty(Class.prototype, accessor, {
419427
get() {
420-
return this.$$c?.[accessor];
428+
if (this.$$c) {
429+
return this.$$c[accessor];
430+
} else {
431+
// This is only an approximation of what's possible.
432+
// It only handles the case where the accessor is a function without a return value
433+
return (...args) => {
434+
this.$$b.push(() => {
435+
this.$$c[accessor](...args);
436+
});
437+
};
438+
}
421439
}
422440
});
423441
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<svelte:options customElement="my-app" />
2+
3+
<script>
4+
export let prop = false;
5+
let innerVar = false;
6+
7+
export function toggle() {
8+
innerVar = !innerVar;
9+
prop = !prop;
10+
}
11+
</script>
12+
13+
<p>{prop} {innerVar}</p>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as assert from 'assert.js';
2+
import { tick } from 'svelte';
3+
import './main.svelte';
4+
5+
export default async function (target) {
6+
const element = document.createElement('my-app');
7+
element.prop = true;
8+
element.toggle();
9+
target.appendChild(element);
10+
await tick();
11+
const el = target.querySelector('my-app');
12+
13+
await tick();
14+
15+
assert.ok(!el.prop);
16+
const p = el.shadowRoot.querySelector('p');
17+
assert.equal(p.textContent, 'false true');
18+
}

packages/svelte/test/runtime-browser/custom-elements-samples/oncreate/main.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<svelte:options customElement="my-app" />
22

33
<script>
4-
import { onMount } from "svelte";
4+
import { onMount } from 'svelte';
55
66
export let prop = false;
77
export let propsInitialized;

0 commit comments

Comments
 (0)