mirror of
https://github.com/sveltejs/svelte.git
synced 2024-11-22 03:47:27 +01:00
Call onMount when connected & clean up when disconnected for custom element (#4522)
* call onDestroy when disconnected * lifecycle hooks and custom elements - Call onMount in connectedCallback for customElements - register onMount return values as on_disconnect-callbacks for customElements - run on_disconnect callbacks in disconnectedCallback * do not reset on_mount so that it can fire again if reinserted * simpler isCustomElement & skip extra function call - pass options.customElement down to mount_component - remove expensive isCustomElement check - only call add_render_callback if not customElement Co-authored-by: Pontus Lundin <pontus.lundin@ica.se>
This commit is contained in:
parent
d3f3ea38d0
commit
d4f98fb63a
@ -485,7 +485,7 @@ export default function dom(
|
||||
|
||||
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
|
||||
|
||||
@init(this, { target: this.shadowRoot, props: ${init_props} }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
|
||||
@init(this, { target: this.shadowRoot, props: ${init_props}, customElement: true }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
|
||||
|
||||
${dev_props_check}
|
||||
|
||||
|
@ -34,6 +34,7 @@ interface T$$ {
|
||||
on_mount: any[];
|
||||
on_destroy: any[];
|
||||
skip_bound: boolean;
|
||||
on_disconnect: any[];
|
||||
}
|
||||
|
||||
export function bind(component, name, callback) {
|
||||
@ -52,23 +53,26 @@ export function claim_component(block, parent_nodes) {
|
||||
block && block.l(parent_nodes);
|
||||
}
|
||||
|
||||
export function mount_component(component, target, anchor) {
|
||||
export function mount_component(component, target, anchor, customElement) {
|
||||
const { fragment, on_mount, on_destroy, after_update } = component.$$;
|
||||
|
||||
fragment && fragment.m(target, anchor);
|
||||
|
||||
// onMount happens before the initial afterUpdate
|
||||
add_render_callback(() => {
|
||||
const new_on_destroy = on_mount.map(run).filter(is_function);
|
||||
if (on_destroy) {
|
||||
on_destroy.push(...new_on_destroy);
|
||||
} else {
|
||||
// Edge case - component was destroyed immediately,
|
||||
// most likely as a result of a binding initialising
|
||||
run_all(new_on_destroy);
|
||||
}
|
||||
component.$$.on_mount = [];
|
||||
});
|
||||
if (!customElement) {
|
||||
// onMount happens before the initial afterUpdate
|
||||
add_render_callback(() => {
|
||||
|
||||
const new_on_destroy = on_mount.map(run).filter(is_function);
|
||||
if (on_destroy) {
|
||||
on_destroy.push(...new_on_destroy);
|
||||
} else {
|
||||
// Edge case - component was destroyed immediately,
|
||||
// most likely as a result of a binding initialising
|
||||
run_all(new_on_destroy);
|
||||
}
|
||||
component.$$.on_mount = [];
|
||||
});
|
||||
}
|
||||
|
||||
after_update.forEach(add_render_callback);
|
||||
}
|
||||
@ -113,6 +117,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
|
||||
// lifecycle
|
||||
on_mount: [],
|
||||
on_destroy: [],
|
||||
on_disconnect: [],
|
||||
before_update: [],
|
||||
after_update: [],
|
||||
context: new Map(parent_component ? parent_component.$$.context : []),
|
||||
@ -155,7 +160,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
|
||||
}
|
||||
|
||||
if (options.intro) transition_in(component.$$.fragment);
|
||||
mount_component(component, options.target, options.anchor);
|
||||
mount_component(component, options.target, options.anchor, options.customElement);
|
||||
flush();
|
||||
}
|
||||
|
||||
@ -173,6 +178,9 @@ if (typeof HTMLElement === 'function') {
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const { on_mount } = this.$$;
|
||||
this.$$.on_disconnect = on_mount.map(run).filter(is_function);
|
||||
|
||||
// @ts-ignore todo: improve typings
|
||||
for (const key in this.$$.slotted) {
|
||||
// @ts-ignore todo: improve typings
|
||||
@ -184,6 +192,10 @@ if (typeof HTMLElement === 'function') {
|
||||
this[attr] = newValue;
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
run_all(this.$$.on_disconnect);
|
||||
}
|
||||
|
||||
$destroy() {
|
||||
destroy_component(this, 1);
|
||||
this.$destroy = noop;
|
||||
|
@ -110,8 +110,8 @@ describe('custom-elements', function() {
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
page.on('console', (type, ...args) => {
|
||||
console[type](...args);
|
||||
page.on('console', (type) => {
|
||||
console[type._type](type._text);
|
||||
});
|
||||
|
||||
page.on('error', error => {
|
||||
|
@ -3,9 +3,12 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let wasCreated;
|
||||
export let prop = false;
|
||||
export let propsInitialized;
|
||||
export let wasCreated;
|
||||
|
||||
onMount(() => {
|
||||
wasCreated = true;
|
||||
});
|
||||
onMount(() => {
|
||||
propsInitialized = prop !== false;
|
||||
wasCreated = true;
|
||||
});
|
||||
</script>
|
||||
|
@ -2,7 +2,9 @@ import * as assert from 'assert';
|
||||
import './main.svelte';
|
||||
|
||||
export default function (target) {
|
||||
target.innerHTML = '<my-app/>';
|
||||
target.innerHTML = '<my-app prop/>';
|
||||
const el = target.querySelector('my-app');
|
||||
|
||||
assert.ok(el.wasCreated);
|
||||
assert.ok(el.propsInitialized);
|
||||
}
|
||||
|
22
test/custom-elements/samples/ondestroy/main.svelte
Normal file
22
test/custom-elements/samples/ondestroy/main.svelte
Normal file
@ -0,0 +1,22 @@
|
||||
<svelte:options tag="my-app"/>
|
||||
|
||||
<script>
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
|
||||
let el;
|
||||
let parentEl;
|
||||
|
||||
onMount(() => {
|
||||
parentEl = el.parentNode.host.parentElement;
|
||||
|
||||
return () => {
|
||||
parentEl.dataset.onMountDestroyed = true;
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
parentEl.dataset.destroyed = true;
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={el}></div>
|
11
test/custom-elements/samples/ondestroy/test.js
Normal file
11
test/custom-elements/samples/ondestroy/test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import * as assert from 'assert';
|
||||
import './main.svelte';
|
||||
|
||||
export default function (target) {
|
||||
target.innerHTML = '<my-app/>';
|
||||
const el = target.querySelector('my-app');
|
||||
target.removeChild(el);
|
||||
|
||||
assert.ok(target.dataset.onMountDestroyed);
|
||||
assert.equal(target.dataset.destroyed, undefined);
|
||||
}
|
@ -40,7 +40,8 @@ class Component extends SvelteElement {
|
||||
this,
|
||||
{
|
||||
target: this.shadowRoot,
|
||||
props: attribute_to_object(this.attributes)
|
||||
props: attribute_to_object(this.attributes),
|
||||
customElement: true
|
||||
},
|
||||
null,
|
||||
create_fragment,
|
||||
|
Loading…
Reference in New Issue
Block a user