Svelte Snacks | Custom Events for Modal Actions

Svelte Snacks | Custom Events for Modal Actions

Here's a quick implementation of Svelte's handy custom events system.

Background

Our users can edit any part of their profile using a dynamic modal editor component.

image.png

User Story

As a user editing my profile, I want to be able to Publish or Cancel when editing any of my profile modules.

Engineering Requirements

  • Make the Save and Cancel button a reusable component
  • Let each component handle the save event because they have unique business logic
  • Let the modal component handle the cancel event

Solution

The first step is to build an EditActionsBar.svelte that is responsible for displaying the Publish and Cancel buttons.

By importing createEventDispatcher, I can dispatch semantic events that make it really clear what the action is, instead of the generic on:click event.

// EditActionBar.svelte

<script>
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();

    let save = function(e) {
        dispatch('save');
    }

    let cancel = function(e) {
        dispatch('cancel');
    }
</script>

<div class="flex flex-row justify-between mt-10">
    <button on:click|preventDefault={cancel} class="cursor-pointer bg-gray-300 rounded-full px-8 py-4">
        Cancel
    </button>
    <button on:click|preventDefault={save} class="cursor-pointer bg-purple-700 text-white rounded-full px-8 py-4">
        <i class="fas fa-check mr-2" /> Publish Changes
    </button>
</div>

This can now be dropped into any of our editor components. We simply forward the on:cancel event and handle the on:save event.

<EditActionsBar on:save={save} on:cancel></EditActionsBar>

For example, this is how we could add it to a simplified version of our EditProfileBody.svelte file.

// EditProfileBody.svelte 

<script>
    import service from '$lib/_services/users.service';
    import TextInput from '$lib/_shared/forms/large/TextInput.svelte';
    import TextArea from '$lib/_shared/forms/large/TextArea.svelte';
    import EditActionsBar from './EditActionsBar.svelte';

    // handlers
    let save = async (e) => {
        const res = await service.editProfile({
            headline,
            description
        });
    };
</script>

<h2 class="text-lg font-medium mb-2">Edit Intro</h2>

<div class="flex flex-col gap-4 mt-8">
    <TextInput
        bind:value={headline}
        name="headline"
        label="Headline"
    />
    <TextArea
        bind:value={description}
        name="description"
        label="Description"
    />
    <EditActionsBar on:save={save} on:cancel></EditActionsBar>
</div>

Conclusion

If you found this format helpful, let me know in the comments or on Twitter @jmzaborowski.