9

So I can't figure a way to pass a variable up in my __layout variable from the component displayed in the <slot>.

I tried a few things, using bind: or let: on the slot but it doesn't work. I get 'myvar' is not a valid binding or <slot> cannot have directives.
I also tried to export or not the variable on the layout, but I really cannot make it work...

Here is what I have:

 <!-- __layout.svelte -->
<script>
export let myvar = undefined;
</script>

<main>
  <slot myvar={myvar}></slot>
  <p>Layout myvar: {myvar}</p>  <!-- << This will stay undefined -->
</main>
 <!-- mypage.svelte -->
<script>
import MyComponent from "$lib/my_component.svelte";
export let myvar;
let a_list_of_things = [1,2,3,4]
</script>

<main>
  {#each a_list_of_things as thing}
    <MyComponent bind:myvar={myvar} thing={thing}/>  <!-- The variable is binded here -->
  {/each}
  <p>mypage myvar: {myvar}</p>   <!-- << This will get the good value -->
</main>
 <!-- my_component.svelte -->
<script>
import IconButton from '@smui/icon-button'

export let myvar;
export let thing;
</script>

<div>
  <IconButton class="material-icons" on:click={() => myvar='something'} >
    autorenew
  </IconButton>  <!-- << We change the value on:click here -->

</div>

So the main goal is to have an equivalent to bind:myvar=myvar on the layout level (for the <slot></slot>.

I tried understanding the documentation about it without much success, it seems to be more about the component slot than the layout one.


I found this other question which advise to use a store in sapper (the old name for sveltekit) not sure it is up to date with the last version of sveltekit, is this the way to go ? Or is there another way ?

Someone else advise using a context. What do you think ?


Why do i need this ?

So I have the structure of my app like this:

__layout
 ├─ header (menu)
 ├─ my_page (<slot>)
 │   └ my_component (many)
 └ interactive banner

That display it like so:

[ Header Menu ]

Content of the page
 - component 1
 - component 2
 - component n

[ Current component: 2 ] << Updated when click on an elem in the component

The component define what the interactive banner should display. Also clicking on the interactive banner can change the state displayed in the components

vinalti
  • 966
  • 5
  • 26

2 Answers2

4

Since this banner message seems to be a piece of global state, a store would be a good solution here. Context would only be necessary if you need to have different banners for different component trees on the page.

Something like this would work:

// src/lib/message.js
import { writable } from 'svelte/store';

export default writable('Default message');
<!-- src/routes/__layout.svelte -->
<script>
    import message from '$lib/message';
</script>

<slot />
<p>{$message}</p>
<!-- src/routes/index.svelte -->
<script>
    import message from '$lib/message';

    function update() {
        $message = 'New message';
    }
</script>

<h1>Hello World</h1>

<button on:click={update}>Update message</button>
Jacob Wood
  • 209
  • 3
  • 13
Geoff Rich
  • 4,726
  • 9
  • 22
  • Thanks a lot, that worked ! Could you add an explanation of the magic behind please ? I think it is important to understand why we do things... – vinalti Jan 31 '22 at 17:09
  • 2
    In `message.js` we create and export Svelte store. Any component can import and subscribe to the store with the `$store` syntax. When the store is updated, anyone subscribing to the store will be notified. This allows us to update the store in `index.svelte` and automatically receive the new value in `__layout.svelte`. I highly recommend going through the [Svelte tutorial on stores](https://svelte.dev/tutorial/writable-stores) if you haven't already, since they're an important Svelte concept. – Geoff Rich Feb 06 '22 at 00:04
  • 2
    This answer clearly gives a solution, but is it the only solution? Is there a way to bind to a or doesn't that work at all? – Kasper Kamperman Apr 17 '23 at 11:07
-1

I give below an example with named slots, also unnamed ->

In Slot.svelte

<script>
  let content ='lorem'
</script>


<slot name='named-slot' >
{content}
</slot>

In Parent component

<script>
 import Data from './Slot.svelte'
 let newData=' new content'
</script>

 
 <Data>  
 <svelte:fragment slot='named-slot' let:newData={content}>
  {newData} 
 </svelte:fragment>

 </Data>

And exmaple for unnamed slot ->

In Slot.svelte

<script>
      let content ='lorem'
    </script>
    
    <slot  >
    {content}
    </slot>

In Parent component

<script>
 import Data from './Slot.svelte'
 let newData=' new content'
</script>
    
 

 <Data let:newData={content}> 
  {newData}
   </Data>
Todor Markov
  • 507
  • 5
  • 12
  • But this doesn't work with a layout, has the slot is defined by the route ? or Am I missing something ? – vinalti Jan 31 '22 at 16:59