svelte
svelte copied to clipboard
$$host (or some way to access custom element instance)
From this Stack Overflow question — it might be nice if there was a way to get a reference to the host element, when compiling to custom elements (i.e. the <my-thing> for tag="my-thing").
Following the precedent set by $$props, we could add a $$host value. The main question is what should happen when not compiling as a custom element — error? or reference to the instance (which we've resisted thus far, because 99% of the time when someone wants that reference it's to do something inadvisable)?
+1
+1
+1
@Rich-Harris do you want a PR on that ?
+1 this could be a great addition as it would enable many cool features
Has there been any more thought on this?
I'd appreciate it if there were a 'reference to the instance'.
I like to organise my Svelte projects with a separation of concerns. The structure (the component template) is mostly separate from the behaviour (default data, lifecycle handlers, event handlers).
As well as being an organisational preference, this approach also integrates well with TypeScript, affording a great degree of safety and IDE integration to my behaviour code.
I was able to do this in Svelte 1 and 2, but this is not currently possible in Svelte 3.
I don't think I'm trying to do anything too funky. I understand that Svelte has become asynchronous. I'm happy to use promises to read and write component properties. I'd just like to be able to write my behaviour outside the component template itself.
If this could be made possible, it would be much appreciated.
I'm using Stencil to create portable custom elements and I really found the way host elements are exposed: https://stenciljs.com/docs/host-element
$$host is being added with #4534 and this is great but do you think adding a svelte:host may be usefull?
I'm building a web component and was desperately needing this. Turns out you can get a reference to the component with get_current_component.
import { get_current_component } from "svelte/internal";
const thisComponent = get_current_component();
I'm building a web component and was desperately needing this. Turns out you can get a reference to the component with get_current_component.
import { get_current_component } from "svelte/internal"; const thisComponent = get_current_component();
yea i use
import {get_current_component} from "svelte/internal";
const component = get_current_component();
component.shadowRoot
or reference to the instance (which we've resisted thus far, because 99% of the time when someone wants that reference it's to do something inadvisable)?
I think this can also be useful to do some inter-Component communication.
I known it's unsafe to use like that because it can break in the future, but it's actually possible via a trick using arguments :
<script lang="ts">
const self = arguments[0];
</script>
Exemple here where i register the component instance to a parent component : https://svelte.dev/repl/46b37e50c482489681925f53312632c4?version=3.57.0
-
Details.svelte is a simple component that wrap a HTML details/summary. It's pretty simple and only provide some accessor, but it also registers itself into a possible parent (via the context).
-
DetailsGroup.svelte is a wrapper component that handle the
<Details>childs that registered. It use the component to register event and manage his props via the accessors.
Details.svelte is mostly independant from DetailsGroup.svelte, but it can be manipulated by the latter for a specific need.
Perhaps we can even use this for that, as it's currently undefined :
<script lang="ts">
console.log(this); // "this" should be the current component instance
</script>
@adiguba
I would definitely agree with you that this comes much naturally to someone who has been writing web components.
Maybe this being a reserved keyboard would be a problem? Going with the svelte guidelines $$this would be better?
I sense confusion above. My interpretation of @Rich-Harris's description is that he wants a reference to the HTMLElement instance, not the component itself.
That said: This varies slightly now in Svelte 4 since you can use shadow root or the light DOM, depending on your customElement options. So, if you're interested in a hack that might work, then this inelegant Satanic incantation might get you where you need to go:
<svelte:options
customElement={{
tag: 'example-element',
shadow: 'none', // Or comment out to enable shadow DOM
}}
/>
<script>
import { onMount } from 'svelte';
let el;
onMount(() => {
const hostEl = el.getRootNode().host || el.parentNode;
console.log('element:', hostEl); // element: <example-element></example-element>
});
</script>
<!-- should be at top level of the component -->
<div bind:this={el}></div>
In the shadow DOM, you could access it by getting a reference to an element and then calling el.getRootNode().host. For the light DOM (Svelte 4) then, from a root level element, use el.parentNode. Not that you should do this. I'm guessing you probably shouldn't do it, but... if you had to, this might be one way. 😅
Documentation here (link for the lazy): https://svelte.dev/docs/custom-elements-api#component-options
You can now pass the host element along using the new extend option:
<svelte:options
customElement={{
tag: 'custom-element',
extend: (customElementConstructor) => {
return class extends customElementConstructor {
constructor() {
super();
this.host = this; // or this.shadowRoot, or whatever you need
}
};
}
}}
/>
<script>
export let host;
</script>
...