Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$$host (or some way to access custom element instance) #3091

Closed
Rich-Harris opened this issue Jun 24, 2019 · 15 comments · Fixed by #8991
Closed

$$host (or some way to access custom element instance) #3091

Rich-Harris opened this issue Jun 24, 2019 · 15 comments · Fixed by #8991

Comments

@Rich-Harris
Copy link
Member

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)?

@vogloblinsky
Copy link

+1

2 similar comments
@a3con
Copy link

a3con commented Jul 5, 2019

+1

@vaheqelyan
Copy link

+1

@vogloblinsky
Copy link

@Rich-Harris do you want a PR on that ?

@linorabolini
Copy link

+1 this could be a great addition as it would enable many cool features

@ChrisTalman
Copy link

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.

@adriengibrat
Copy link

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?

@madupuis90
Copy link

madupuis90 commented Jun 3, 2021

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();

@vospascal
Copy link

vospascal commented Jul 3, 2021

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 

@adiguba
Copy link
Contributor

adiguba commented Mar 18, 2023

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.

@adiguba
Copy link
Contributor

adiguba commented Mar 18, 2023

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>

@tronicboy1
Copy link

@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?

@dummdidumm dummdidumm removed this from the 4.x milestone Jun 19, 2023
@patricknelson
Copy link

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. 😅

dummdidumm added a commit that referenced this issue Jul 18, 2023
This should help everyone who has special needs and use cases around custom elements. Since Svelte components are wrapped and only run on connectedCallback, it makes sense to expose the custom element class for modification before that.
- fixes #8954 - use extend to attach the function manually and save possible values to a prop
- closes #8473 / closes #4168 - use extend to set the proper static attribute and then call attachInternals in the constructor
closes #8472 - use extend to attach anything custom you need
closes #3091 - pass `this` to a prop of your choice and use it inside your component
@patricknelson
Copy link

Documentation here (link for the lazy): https://svelte.dev/docs/custom-elements-api#component-options

@dummdidumm
Copy link
Member

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>

...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.