Félix Paradis
Félix Paradis

Reputation: 6031

How can I pass parameters to on:click in Svelte?

Binding a function to a button is easy and straightforward:

<button on:click={handleClick}>
    Clicks are handled by the handleClick function!
</button>

But I don't see a way to pass parameters (arguments) to the function, when I do this:

<button on:click={handleClick("parameter1")}>
    Oh no!
</button>

The function is called on page load, and never again.

Is it possible at all to pass parameters to function called from on:click{}?


I found the proper way to do it (see comments). Calling the function from an inline handler works.

<button on:click={() => handleClick("parameter1")}>
    It works...
</button>

Upvotes: 123

Views: 95619

Answers (9)

V. Sambor
V. Sambor

Reputation: 13389

TL;DR

Just wrap the handler function in another function.


You have to use a function declaration and then call the handler with arguments. The arrow function is elegant and is good for this scenario.

Why do I need another function wrapper?

If you would use just the handler and pass the parameters, how would it look like?

Probably something like this:

<button on:click={handleClick("arg1")}>My awesome button</button>

But remember, handleClick("arg1") this is how you invoke the function instantly, and that is exactly what is happening when you put it this way. It will be called when the execution reaches this line, and not as expected, on a button click...

Therefore, you need a function declaration, which will be invoked only by the click event and inside it you call your handler with as many arguments as you want.

<button on:click={() => handleClick("arg1", "arg2")}>
    My awesome button
</button>

As Rich Harris (the author of Svelte) pointed out in a comment: This is not a hack, but it is what the documentation shows also in their tutorials: Events/Inline handlers

Upvotes: 134

Antony Jones
Antony Jones

Reputation: 3180

Rich has answered this in a comment, so credit to him, but the way to bind parameters in a click handler is as follows:

<a href="#" on:click|preventDefault={() => onDelete(projectId)}>delete</a>
<script>
    function onDelete (id) {
      ...
    }
</script>

To provide some extra detail for people who also struggle with this, and it should be in the docs if it isn't, you can also get the click event in such a handler:

<a href="#" on:click={event => onDelete(event)}>delete</a>
<script>
    function onDelete (event) {
      // If it's a custom event you can get the properties passed to it:
      const customEventData = event.detail

      // If you want the element, you guessed it:
      const targetElement = event.target
      ...
}
</script>

Svelte documentation/tutorial: inline handlers

Upvotes: 37

n-smits
n-smits

Reputation: 713

A Pug solution

Assign with !=. Yes, assign with !=. It is weird as hell. Example: Foo(on:click!='{() => click(i)}'). This is per documentation for svelte-preprocess (a necessary transpiler to include templates in Pug):

Pug encodes everything inside an element attribute to html entities, so attr="{foo && bar}" becomes attr="foo &amp;&amp; bar". To prevent this from happening, instead of using the = operator use != which won't encode your attribute value [...] This is also necessary to pass callbacks.

Personally, I will use a utility function for this, because assigning with != bothers me too much.

// Utility function
const will = (f, v) => () => f(v);
// In a Pug template, we can again assign with =
Foo(on:click='{will(click, i)}')

Upvotes: 3

chovy
chovy

Reputation: 75686

I got it working with this:

<a href="#" on:click|preventDefault={onDelete.bind(this, project_id)}>delete</a>
function onDelete(id) {
}

Upvotes: 2

chovy
chovy

Reputation: 75686

Here it is with a debounce method and event argument:

<input type="text" on:keydown={event => onKeyDown(event)} />
const onKeyDown = debounce(handleInput, 250);

async function handleInput(event){
    console.log(event);
}

Upvotes: 0

Yuzem
Yuzem

Reputation: 27

I'm using:

{#each data as item}
   <li on:click={handle(item)}>{item.name}</li>
{/each}

...and it is not running the function when it renders; it works on click.

Upvotes: -1

emperorz
emperorz

Reputation: 427

Check out sortable-table.

Note the passing of parameters to the header click handling function in a magical way. I don't have any idea why this is so. Auto-currying maybe?

Upvotes: -1

Stephane Vanraes
Stephane Vanraes

Reputation: 16411

There isn't any clear way mentioned in the documentation and your solution will work, but it is indeed not very elegant. My own preferred solution is to use currying in the script block itself.

const handleClick = (parameter) => () => {
   // Actual function
}

And in the HTML:

<button on:click={handleClick('parameter1')>
   It works...
</button>

Beware of currying

As mentioned in the comments, currying has its pitfalls. The most common one that in the above example handleClick('parameter1') will not be fired when clicking, but rather on rendering, returning a function that in turn will be fired onclick. This means that this function will always use 'parameter1' as its argument.

Therefore using this method would only be safe if the used parameter is a constant of some kind and will not change once it's rendered.

This would bring me to another point:

  1. If it's a constant used a parameter, you could as well use a separate function

    const handleParameter1Click = () => handleClick('parameter1');
    
  2. If the value is dynamic but available within the component, this could still be handled with a stand-alone function:

    let parameter1;
    const handleParameter1Click = () => handleClick(parameter1);
    
  3. If the value is dynamic, but not available from the component, because this is dependent on some kind of scope (e.g., a list of items rendered in a #each block) the 'hacky' approach will work better. However, I think it would be better to in that case have the list-elements as a component themselves and fall back to case #2

To conclude: currying will work under certain circumstance, but it is not recommended unless you are very well aware and careful about how to use it.

Upvotes: 7

tetshi
tetshi

Reputation: 50

It appears to work in this particular situation. I don't know enough to understand why, but I thought it was relevant since even Rich says it shouldn't. I couldn't find any reason after searching that made this case unique.

REPL

Upvotes: -1

Related Questions