Reputation: 63405
I want to developt my own Button component and be able to handle event modifiers, like this:
<MyButton on:click|preventDefault={handler}>Click me</MyButton>
But I get the following error:
Event modifiers other than 'once' can only be used on DOM elementssvelte(invalid-event-modifier)
In MyButton I can pass the on:click event like this:
<button on:click|preventDefault>
<slot />
</button>
But then I won't be able to use MyButton without the preventDefault
So another option would be to optionally pass event modifiers, to do something like this:
<MyButton preventDefault on:click={handler}>Click me</MyButton>
And then in MyButton.svelte to something like this (I know this doesn't work) to optionally apply the event modifier.
<script>
export let prevenDefault=false
</script>
<button on:click|{preventDefault ? 'preventDefault' : ''}={handler}>Click me</MyButton>
Any idea about how to deal with it?
Upvotes: 8
Views: 3656
Reputation: 13
Here is an alternative that works, at least for me:
<button on:click|preventDefault>
<MyButton on:click={handleClick}>
Click Me
</MyButton>
</button>
Upvotes: 0
Reputation: 105
Custom button component
<script lang="ts">
export let preventDefault: boolean = false;
export let stopPropagation: boolean = false;
type MouseEvent = Parameters<MouseEventHandler<HTMLButtonElement>>[0];
type $$Events = {
click: MouseEvent;
};
</script>
{#if preventDefault}
{#if stopPropagation}
<button {...$$restProps} on:click|preventDefault|stopPropagation
><slot /></button>
{:else}
<button {...$$restProps} on:click|preventDefault><slot /></button>
{/if}
{:else if stopPropagation}
<button {...$$restProps} on:click|stopPropagation><slot /></button>
{:else}
<button {...$$restProps} on:click><slot /></button>
{/if}
Custom button component usage
<Button
type="button"
class="primary-button"
title="this is a button"
on:click={onClick}
preventDefault
stopPropagation
>
Send complaint
</Button>
If you can endure to insane branch statements drive your brain crazy, this code should achieve what you're looking for. I know. this is really poor workaround. I really want Svelte has a better way of doing this.
Upvotes: 3
Reputation: 375
There are two ways that I think are applicable:
Pros | Cons |
---|---|
Easier & cleaner to implement | Consumers of the component will have to call Event#preventDefault imperatively |
In order to set this up, you'll need two things.
<Button>
component to be forwarding its event:<script>
// Button.svelte
</script>
<!-- Note that we're not providing any callback, which forwards it -->
<button on:click>
<slot />
</button>
Event#preventDefault
on the event:<script>
// App.svelte
import Button from './Button.svelte';
</script>
<Button
on:click={(event) => {
event.preventDefault();
// your code here
}}
>
Click Me!
</Button>
Pros | Cons |
---|---|
Declaratively add event modifier | Consumers of the component will unwrap the original Event object from CustomEvent#detail |
You will also need two things here.
<Button>
component to add its callback manually in the <script>
tag<script>
// Button.svelte
import {
onMount,
onDestroy,
createEventDispatcher,
} from 'svelte';
export let preventDefault = false;
let button;
const dispatch = createEventDispatcher();
onMount(() => {
button.addEventListener('click', onClick);
});
onDestroy(() => {
button.removeEventListener('click', onClick);
});
function onClick(event) {
if (preventDefault) event.preventDefault();
dispatch('click', event);
}
</script>
<button bind:this={button}>
<slot />
</button>
preventDefault
prop<script>
// App.svelte
import Button from './Button.svelte';
</script>
<Button
preventDefault
on:click={({ detail: event }) => {
// your code here
})
>
Click me!
</Button>
Upvotes: 8