Reputation: 533
I'm new to Svelte. I have 2 Svelte components in an HTML parent-child relationship – as opposed to a Svelte P/C relationship (where 1 Svelte component imports another).
Ultimately, I want something like this (could have many Accs.):
<Accordion header={--property from SvelteComponent-- }>
<SvelteComponent />
</Accordion>
I would like the SvelteComponent to define the header used in the Accordion (it can be dynamic). I can use stores for this but that seems too messy. Accordion does contain a slot but I can’t see how to pass information upward. Can anyone suggest a path forward?
Upvotes: 13
Views: 20147
Reputation: 533
Svelte is great at getting rid of boilerplate and unnecessary extra code. I want to take that as far as I can.
I believe using @skeletizzle answer that I would have to add a variable to the container of the Accordions, for each Accordion and its child. THAT is what I want to eliminate (it pollutes the namespace of the container for a trivial operation). Since the Accordion and its child are in a P/C relation - they know about each other and the compiler could make communication direct. Think of an implied interface where the Accordion looks in its child and it it finds a prop named, say 'header', automatically, reactively, uses that for its own header prop.
What I am going to do is use stores though as stated, I feel they are too heavy - but don't think I have an alternative.
WrappedComponent.svelte
<script context="module">
import { writable } from 'svelte/store'
export let thisComponentHeader = writable('default header')
</script>
App.svelte
import WrappedComponent, { thisComponentHeader } from './WrappedComponent.svelte'
...
<Accord header={$thisComponentHeader}>
<WrappedComponent />
</Accord>
There still is a variable. but it is socked away in the import definition
One thing this prevents is having more than 1 instance of the component.
Upvotes: 2
Reputation: 251
One option is to use component bindings. This allows a two-way binding between a value in the scope of a container component and a prop of the contained component.
<Accordion {header}>
<SvelteComponent bind:header={header} />
</Accordion>
and in SvelteComponent.svelte
:
<script>
export let header = "defaultHeader";
</script>
Whenever SvelteComponent makes a change to defaultHeader, it will propogate back up via the binding to the file which contains the Accordion and SvelteComponent, and apply the changes back downward. https://svelte.dev/tutorial/component-bindings
Alternatively, you can provide a setHeader
function as a prop to SvelteComponent, which sets the value of header:
//SvelteComponent.svelte
<script>
export let setHeader;
</script>
<div on:click={() => setHeader("myHeader")}>
My Svelte Component
</div>
Upvotes: 25
Reputation: 41
What about store
and context
management? Instead of creating module level store, set it in the component context. https://svelte.dev/docs#run-time-svelte-setcontext
Accordion.svelte
<script>
import {writable} from "svelte/store";
import {setContext} from "svelte";
const header = writable('Default header');
setContext('header', header)
</script>
<div>{$header}</div>
WrappedComponent.svelte
<script>
import {getContext} from 'svelte';
const header = getContext('header');
if(header) $header = 'New fancy header'
</script>
App.svelte
<Accordion>
<WrappedComponent />
</Accordion>
Upvotes: 3