14

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.

10
  • Let every component define and add it's own on:click handler. And have a look at <svelte:component> Commented Sep 26, 2019 at 6:39
  • I want to avoid the duplicated code within each component by having an outter component supply a toolbar which passes the click event into the child to deal with the click. I didn't think something like this would be so difficult when everything else in Svelte is so easy. Commented Sep 26, 2019 at 6:45
  • Why pass a click event. Every component can handle it's own handler. And if A, B, ... not identical sub somponents you can always share code by importing a common js file. Commented Sep 26, 2019 at 7:27
  • And if you have a click event from multiple sources in the parent component you can identify the source using the event target and share the action with the nested components uning a prop or store. Commented Sep 26, 2019 at 7:40
  • The components are indeed all different and using a common.js file may help in this specific example but I think there is still a valid use case for wanting to pass the event into the component. Passing a prop would also work but unfortunately this does not work either when using dynamically generated components as shown in the following modified REPL - svelte.dev/repl/fc91e089278848eba782f9ef994f534e?version=3.12.1 Commented Sep 26, 2019 at 11:24

1 Answer 1

21

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>
Sign up to request clarification or add additional context in comments.

3 Comments

Hi Rich, Your 2nd suggestion is perfect for what I need and it is much appreciated. Thank you also for Svelte (which is totally awesome).
Yes, very nice.
After a lot of searching, this answer showed me that using export is necessary when calling functions on child components, finally I can stop banging my head on the keyboard. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.