Reputation: 114817
I try to wrap an application into a 'Repository' component and that outer component should handle all communication with the backend, like loading, updating, deleting editable data. I thought I could just send events up to that Repository
component but I doesn't work the way I do it. Does anyone spot the issue or can explain, why this doesn't work and how to do it correctly with events? I could use writables instead but events would make it more readable. Here's a simplified example and a svelte REPL link:
https://svelte.dev/repl/e1eae56c7d5e48b2a99299f1bc1bf970?version=3.22.3
App.svelte:
<script>
import Repository from './Repository.svelte'
import Application from './Application.svelte'
</script>
<Repository>
<Application on:save={() => console.log('caught in App')} />
</Repository>
Repository.svelte:
<div on:save={() => console.log('caught in Repository')}>
<slot></slot>
</div>
Application.svelte:
<script>
import {createEventDispatcher} from 'svelte'
const dispatch = createEventDispatcher();
function saveHandler() {
console.log('dispatching')
dispatch('save')
}
</script>
<button on:click={saveHandler}>
Save
</button>
The desired output would be
dispatching
caught in Repository
but it only prints
dispatching
caught in App
when the button is clicked.
Upvotes: 5
Views: 4507
Reputation: 3436
The cleanest solution I have found is to use HTML-Events instead of Svelte dispatched events. HTML Events can bubble up through the DOM and can be catched by parent components.
On the child:
node.dispatchEvent(new CustomEvent('save', { bubbles: true }))
On the parent:
node.addEventListener('save', saveHandler)
Svelte REPL with example: https://svelte.dev/repl/b5f5e41bae704e6a83fc0cf8c0f58f57
Upvotes: 3
Reputation: 114817
Found at least one solution, the let
directive can help, although it's no pure eventing and pretty verbose.
https://svelte.dev/repl/a03214d522cd4bcba67f86c426a3b28d?version=3.22.3
App.svelte:
<script>
import Repository from './Repository.svelte'
import Application from './Application.svelte'
</script>
<Repository let:saveHandler={saveHandler}>
<Application on:save={saveHandler} />
</Repository>
Repository.svelte:
<script>
function saveHandler() {
console.log('caught in Repository')
}
</script>
<div>
<slot {saveHandler}></slot>
</div>
Application.svelte:
<script>
import {createEventDispatcher} from 'svelte'
const dispatch = createEventDispatcher();
function saveHandler() {
console.log('dispatching')
dispatch('save')
}
</script>
<button on:click={saveHandler}>
Save
</button>
The con (or pro) is that we have to expose all methods from the Repository with let
directives on the main component and also catch the events there, which can be a lot of unwanted event forwarding (because events only bubble up one level).
Upvotes: 9