Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing on:click event into dynamically created <svelte:component/>

I basically need to be able to trigger something within one or more components (that are being dynamically added via svelte:component) when an icon/button within the parent component is clicked. e.g. I need to hook the parts denoted with ** below:-

<script>
 let charts = [
    ChartA,
    ChartB,
    ChartC
  ];
</script>
{#each charts as chart, i}
    <div class="wrapper">
        <div class="icon" on:click={**HowToPassClickEventToComponent**}></div>
        <div class="content">
        <svelte:component this={charts[i]} {**clickedEvent**}/>
        </div>
    </div>
{/each}

I was able to get something working by unsing an array of props but each component is notified when the array changes so this is not very clean.

I have searched both Google and StackOverflow as well as asking this question within the Svelte Discord channel with currently no luck.

Svelte Repl showing the problem

This seems like such a simple requirement but after a couple of days I remain stuck so any advice on how to pass events into dynamic components is much appreciated.

like image 307
Minty Avatar asked Sep 26 '19 05:09

Minty


1 Answers

You could do it like this:

<script>
    import ChartA from './ChartA.svelte'
    import ChartB from './ChartB.svelte'
    import ChartC from './ChartC.svelte'
    let charts = [
        ChartA,
        ChartB,
        ChartC
    ];
    let events = [];
</script>

<style>
    .icon{
        width:60px;
        height:30px;
        background-color:grey;
    }
</style>

{#each charts as chart, i}
    <div class="wrapper">
        <div class="icon" on:click={e=>events[i] = e}>Click</div>
        <div class="content">
            <svelte:component this={charts[i]} event={events[i]}/>
        </div>
    </div>
{/each}

Passing events around as data would be a bit unusual though. It would be more idiomatic to set some state in the parent component in response to the event, and pass that state down.

Alternatively, if the child components do need to respond to events themselves you could take this approach...

<script>
    import ChartA from './ChartA.svelte'
    import ChartB from './ChartB.svelte'
    import ChartC from './ChartC.svelte'
    let charts = [
        ChartA,
        ChartB,
        ChartC
    ];
    let instances = []; 
</script>

<style>
    .icon{
        width:60px;
        height:30px;
        background-color:grey;
    }
</style>

{#each charts as chart, i}
    <div class="wrapper">
        <div class="icon" on:click={e => instances[i].handle(e)}>Click</div>
        <div class="content">
            <svelte:component
                this={charts[i]}
                bind:this={instances[i]}
            />
        </div>
    </div>
{/each}

...where each child component exports a handle method:

<script>
    let event;
    export function handle(e){
        event = e;
    };
</script>
like image 200
Rich Harris Avatar answered Oct 04 '22 21:10

Rich Harris