DEV Community

Cover image for Svelte journey | Props, Template Directives, Events
Denys Sych
Denys Sych

Posted on • Edited on

Svelte journey | Props, Template Directives, Events

Welcome,
this is the second part of our amazing Svelte journey. Previously, we’ve learned about the basics, template and reactivity. Today, we'll delve into Props, Template Directives, and Events. So, let’s start.

Props

⚠️ export keyword is not regular JS export

export marks that a variable is an external, input property.

<script>
    export let foo; // 'export' means input prop
    export let marko = 'Polo'; //  input prop with default value
</script>
Enter fullscreen mode Exit fullscreen mode

{…spread} props

Pretty same as in React:

<PackageInfo
    name={pkg.name}
    speed={pkg.speed}
    version={pkg.version}
    website={pkg.website}
/>
Enter fullscreen mode Exit fullscreen mode
<PackageInfo {...pkg} />
Enter fullscreen mode Exit fullscreen mode

$$props — last resort stuff 🚩

Is needed for reference all props (including also properties, that were not mentioned as inputs (export let <prop name>) for the component).

  • Feels like (…props: any[]) or JS function’s arguments;
  • $$restProps allows to do the same but only for props, that were not mentioned as inputs;
  • Not recommended, difficult for Svelte to optimise.

Template Directives

// Conditional rendering
{#if count > 10}
    <p>{count} is greater than 10</p>
{:else if count < 5}
    <p>{count} is less than 5</p>
{:else}
    <p>{count} is between 5 and 10</p>
{/if}
// Iterables (any generic iterables) rendering
{#each colors as color, i (color)} // ", i" is optional; (*) is key, also optional
    <span>{color}, idx: {i}</span>
{/each}

Enter fullscreen mode Exit fullscreen mode

Key for lists rendering

  • similar to other libs/frameworks, optimises DOM updates;
  • any object can be key, but string or number is safer. So keep it safe.
// Key usage samples
{#each things as thing, i (thing.id)}
    <Thing name={thing.name + " - " + i}  />
{/each}
{#each things as thing, i (i)}
    <Thing name={thing.name + " - " + i}  />
{/each}

Enter fullscreen mode Exit fullscreen mode

await blocks for async operations 🍭

  • Kinda async pipe from Angular or useQuery from tanstack's library
  • Only the most recent promise is considered — new value comes into variable and cancels previous pending state if some.
// Basic async stuff
{#await promise}
    <p>...waiting</p>
{:then number}
    <p>The number is {number}</p>
{:catch error}
    <p style="color: red">{error.message}</p>
{/await}

// if you're sure there are no rejects
{#await promise then number}
    <p>The number is {number}</p>
{/await}

Enter fullscreen mode Exit fullscreen mode

Events

<script>
    let m = { x: 0, y: 0 };

    function handleMove(event) {
        m.x = event.clientX;
        m.y = event.clientY;
    }
</script>

<div
    on:pointermove={handleMove}  // Handler fn.
    on:click={(e) => {           // Inline handler
        console.log(m, e.target);
    }}
>
    The pointer is at {m.x} x {m.y}
</div>

Enter fullscreen mode Exit fullscreen mode

Modifiers for event handlers 🍰

Pipelike handy notation to apply modifiers:

<button on:click|once={() => alert('clicked')}>
    Click me
</button>
<button on:click|once|self|capture={() => alert('you can chain them!')}>
    Click me
</button>

Enter fullscreen mode Exit fullscreen mode

Available modifiers:

  • preventDefault;
  • stopPropagation;
  • passive — about scrolling. Svelte handles it automatically;
  • nonpassive — explicitly set passive: falsecapture;
  • once;
  • self — only trigger handler if event.target is the element itself;
  • trusted — only trigger handler if event.isTrusted is true, meaning it is a action performed by human (click) rather than by code.

Component events

You need to:

  • have a name for an event;
  • dispatch this event via special dispatcher service, that is provided by svelte;
  • assign to events and handle them.

Here is a simple example:

// Inner.svelte --------------------------------------------------------------
<script>
    import { createEventDispatcher } from 'svelte'; // the service

    const dispatch = createEventDispatcher(); // we created dispatcher

    function doSomething() { 
        dispatch('somethingHappened', { // dispatch 'somethingHappened' event
            action: 'You did it!'
        });
    }
</script>

<button on:click={doSomething}> // click will trigger event dispatch
    Do it.
</button>
// Outer.svelte --------------------------------------------------------------
...
<Inner on:somethingHappened={handleEvent} /> // listen to an event
...
Enter fullscreen mode Exit fullscreen mode

And this example is more broad, it depicts how you can forward dispatches and how to handle not only component events, but also DOM events:

// App.svelte ------------------------------------------------------------
<script>
    import Outer from './Outer.svelte';
    import CustomButton from './CustomButton.svelte';

    function handleMessage(event) {
        alert(event.detail.action);
    }

    function handleClick(event) {
        alert("Polo!");
    }

</script>
<Outer on:somethingHappened={handleMessage} />

<CustomButton on:click={handleClick} />
// Outer.svelte ------------------------------------------------------------
<script>
    import Inner from './Inner.svelte';
    import { createEventDispatcher } from 'svelte';

    const dispatch = createEventDispatcher();

    function forward(e) {
        dispatch('somethingHappened', e.detail);
    }
</script>

<Inner on:somethingHappened={forward} />
// Inner.svelte ------------------------------------------------------------
<script>
    import { createEventDispatcher } from 'svelte';

    const dispatch = createEventDispatcher();

    function doSomething() {
        dispatch('somethingHappened', {
            action: 'And something happened!'
        });
    }
</script>

<button on:click={doSomething}>
    Time has come
</button>
// CustomButton.svelte -------------------------------------------------------
<script>
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();

    function forwardClick(e) {
        dispatch('click', e)
    }
</script>
<button on:click={forwardClick}>
    Marko
</button>

<style>
    button {
            background: navajowhite;
            padding: 4px 8px;
    }
</style>

Enter fullscreen mode Exit fullscreen mode
  • createEventDispatcher must be called at the initialisation;
  • component events don't bubble — you need to forward them;
  • DOM event forwardingthe same as component’s events.

Amazing REPL, Amazing CLI

The code snippets quickly outgrew the tutorial's IDE, prompting me to seek alternatives. I turned to the Svelte REPL, an online environment that seemed perfect and even offered PWA installation. However, my intention was to experiment without registration, which unfortunately wasn't allowed.

Undeterred, I chose to download it locally using npm create svelte@latest my-app, and the experience was remarkably convenient 🏅. However, my initial excitement dwindled when npm install raised concerns about broken dependencies. While forcing it was an option, opting for stability with a non-beta version proved to be the solution. It serves as a valuable reminder that only reliable components should make their way into production.

In the end, everything is running smoothly.

Take care, go Svelte!

Resources

Top comments (3)

Collapse
 
tumivn profile image
Tumi Le (Lê Hoàng Dũng)

Thank you for a very informative Sveltekit series. I've learnt a lot.

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

When doing multiple parts of a topic, you are best off with the Series feature of Dev.to.

Collapse
 
chillyhill profile image
Denys Sych

Hey José,

Thanks a bunch for the hint! Got it fixed.