I have a hierarchy of the nested components (Svelte 3.35.0
), which looks like this:
Component A
Component B (third-party)
Component C
Component C wants to dispatch
an event using createEventDispatcher
and Component A is listening. The documentation says:
If the on: directive is used without a value, the component will forward the event, meaning that a consumer of the component can listen for it.
The problem is.. default behavior restricts event forwarding to "Child -> Parent" and I don't have access to the Component B's code (it is a separate library). According to the rules, I need to "forward" that event up on the defined hierarchy, i.e. from B to A.
So, if I want to properly "bubble" an event from Component C to the handler, which is defined in the Component A, I need to build the following logic:
Component A have an implementation for the "onMyEventHandler" function and
it doesn't know anything about Component C (it shouldn't, for decoupling purposes).
Component A ---------------------- on:my-event={onMyEventHandler}
Component B ------------------ on:my-event <-- just to forward up to the parent's parent
Component C -------------- dispatch('my-event')
The question is - how to properly pass an event from the Component C to the Component A using on:eventname
directive? I've seen a bubble
method in the lifecycle code (https://github.com/sveltejs/svelte/blob/v3.35.0/src/runtime/internal/lifecycle.ts#L64), but I don't understand how to properly utilize that logic. Or is there a more convenient approach?
Update: there are also "magic" props like $$props
and $$restProps
, which are used to forward values for the component properties via element attributes, but there are no similar "tricks" for on:
-like directives.
Update 2: so, I should provide an example how it is possible (also, at this point i'm considering to use a writable
store and contexts instead).
I'm using a svelte-routing
library and my code looks like this:
./Application.svelte <--- Component A
<Menu items={menuItems} />
...
<Router>
{#each menuItems as menuItem}
{#if menuItem.component !== MenuDivider}
<Route <--- Component B
path={menuItem.path}
component={menuItem.component} <--- Component C
on:app.event.page.shown={onPageShown}
/>
{/if}
{/each}
</Router>
./Navigation/Menu.svelte
<Router>
<ul class="menu">
{#each items as item}
{#if item.component === MenuDivider}
<MenuDivider label={item.title} />
{:else}
{#if item.url?.length > 0}
<li class="menu-item">
<Link to={item.url} getProps={onLinkUpdate}>
{item.title}
</Link>
</li>
{/if}
{/if}
{/each}
</ul>
</Router>
./Page/AboutPage.svelte
<div> page contents... </div>
Route
is a "third-party" component from the external library that provides API to define client-side routing between different "virtual" pages (i.e. page components, see the line component={menuItem.component}
). The Page have an onMount
with some business logic to inform the main application components about different state changes (e.g. title appends). So it emits events and the top-level application component will try to catch them (and it doesn't know how much pages we have and their names).
Route
from the svelte-routing
has the following signature:
{#if $activeRoute !== null && $activeRoute.route === route}
{#if component !== null}
<svelte:component this="{component}" location={$location} {...routeParams} {...routeProps} />
{:else}
<slot params="{routeParams}" location={$location}></slot>
{/if}
{/if}
and if I place a forwarding on:onMyEventHandler
directive to it like that:
<svelte:component this="{component}" location={$location} {...routeParams} {...routeProps} on:onMyEventHandler />
It will propagate an event using a path: AboutPage (project-level) -> Route (library) -> Application (project-level)
. But, obviously, I'm not able to do that (and there are no "$$restDirectives"/"$$forwardMePlease" trick to do this).
I think it is just a bad way to do things, thought that an "event" concept is a global thing (coming from java/php-style event dispatchers).
3.24.0
) - it was possible (don't know how). After upgrading to the3.35.0
- my code with event dispatching logic doesn't work anymore :) – itnelo3.24.0
), but B is just a "bridge" between routing library and a "page component" (C), which emits events for the main application component (A), e.g. to change a shared page title. – itneloon:
style events. If this is not set in stone, you could use custom dom events like in my example at: svelte.dev/repl/f1b23c6b95be4dfb9cad18dbba20d1f1?version=3.35.0 – Christian