I am looking for a way to listen to a dispatched event from a Svelte component within another component from JavaScript (and not from the on:
syntax).
Here is the code I am trying to achieve on REPL.
The expected behaviour would be to have 0 displayed in the console when the button Close 0 is clicked, and so on for the other ones.
I went through some digging in svelte's compiled code, and I found a solution to listen on svelte's handled event, but it's not pretty :)
You can (and should) dispatch your own custom event when calling onClose
, but here's the solution:
on Nested.svelte
<script context="module">
let counter = 0
</script>
<script>
import { createEventDispatcher, onMount } from 'svelte';
// add this
import { get_current_component } from 'svelte/internal';
let _this;
const id = counter++
const dispatch = createEventDispatcher()
/*********
and add this reactive statement
**********/
$: {
if (_this){
_this.parentNode.hosts = (_this.parentNode.hosts || []);
_this.parentNode.hosts.push(get_current_component());
}
}
/*********
end
**********/
function onClose() {
dispatch('close', id)
}
</script>
<!-- bind this -->
<button bind:this={_this} class='nested-button' on:click={onClose}>
Close {id}
</button>
Then in your App.svelte
<script>
import { onMount } from 'svelte'
import Nested from './Nested.svelte'
let element
onMount(() => {
// requestAnimationFrame is required!
requestAnimationFrame(() => element.hosts.forEach(nestedButton => {
nestedButton.$on('close', (e) => {
console.log(e.detail)
})
}));
})
</script>
<ul bind:this={element}>
<Nested/>
<Nested />
<Nested />
</ul>
Explanation -
the only way to bind to a svelte event is by getting a reference to the calling component and calling the $on
method, but currently there's no easy way of getting a component's reference.
so what I did was calling the svelte's internal get_current_component
, which will return the current component (but for some reason won't work when called inside onMount
).
I appended the reference to the parentNode.hosts
of the top most element in the component, which in this case is the ul
element.
then you can just call the $on
method for each reference in element.hosts
.
The appropriate solution however will be dispatching you own event like this:
function onClose() {
dispatch('close', id)
this.dispatchEvent(new CustomEvent('close', {detail: id}));
}
And by that achieving almost the exact same thing without messing with svelte's internals