Kurs Svelte i SvelteKit · Moduł 4: Props i zdarzenia

Events - Komunikacja w górę

3 min czytania ·
W tej lekcji4

Events pozwalają komponentom potomnym wysyłać informacje do rodziców - jak raporty zwiadowców do jarla! W Svelte 5 robi się to zwykłymi funkcjami przekazanymi w props.

Callback props (Svelte 5)

Rodzic przekazuje funkcję jako prop, a dziecko wywołuje ją w atrybucie zdarzenia, np. onclick:

<!-- WarriorCard.svelte -->
<script>
  let { warrior, onSelect, onDelete } = $props();
</script>

<div class="card">
  <h3>{warrior.name}</h3>
  <button onclick={() => onSelect(warrior)}>
    Wybierz
  </button>
  <button onclick={() => onDelete(warrior.id)}>
    Usuń
  </button>
</div>
<!-- Parent.svelte -->
<script>
  import WarriorCard from './WarriorCard.svelte';

  let warriors = $state([
    { id: 1, name: 'Ragnar' },
    { id: 2, name: 'Bjorn' }
  ]);

  let selected = $state(null);

  function handleSelect(warrior) {
    selected = warrior;
    console.log('Wybrano:', warrior.name);
  }

  function handleDelete(id) {
    warriors = warriors.filter(w => w.id !== id);
  }
</script>

{#each warriors as warrior (warrior.id)}
  <WarriorCard
    {warrior}
    onSelect={handleSelect}
    onDelete={handleDelete}
  />
{/each}

{#if selected}
  <p>Wybrany: {selected.name}</p>
{/if}

Nazwy takich props zaczynaj od on (onSelect, onDelete) - od razu widać, że to zdarzenia. Dane trafiają do rodzica wprost jako argumenty funkcji.

Zdarzenia elementów i modyfikatory

Zdarzenia DOM to w Svelte 5 zwykłe atrybuty: onclick, onkeydown, onsubmit. Modyfikatorów w stylu |preventDefault już nie ma - wywołujesz odpowiednią metodę w handlerze:

<script>
  let report = $state('');

  function sendReport(e) {
    e.preventDefault(); // dawniej on:submit|preventDefault
    report = 'Raport wysłany do jarla!';
  }
</script>

<form onsubmit={sendReport}>
  <input placeholder="Treść raportu" />
  <button>Wyślij</button>
</form>
<p>{report}</p>

Przekazywanie zdarzeń dalej (forwarding)

Komponent, który owija element (np. przycisk), przyjmuje callback w props i oddaje go elementowi:

<!-- Button.svelte -->
<script>
  let { onclick, children } = $props();
</script>

<button {onclick}>
  {@render children()}
</button>

Możesz też przekazać elementowi wszystkie pozostałe props naraz - razem z onclick, class czy disabled:

<!-- Button.svelte -->
<script>
  let { children, ...rest } = $props();
</script>

<button {...rest}>
  {@render children()}
</button>
<!-- Parent.svelte -->
<script>
  import Button from './Button.svelte';

  function handleClick() {
    alert('Do boju!');
  }
</script>

<Button onclick={handleClick}>
  Kliknij mnie
</Button>

Przez komponent pośredni callback przechodzi jak każdy inny prop: App przekazuje onAttack do Battle, a Battle dalej do Fighter (zobacz edytor poniżej).

Svelte 4 (legacy): createEventDispatcher

W starszym kodzie zobaczysz createEventDispatcher i dyrektywy on:. W Svelte 5 createEventDispatcher jest przestarzały, a zamiast <button on:click> (forwarding bez handlera) przekazuje się callback jak wyżej. Rozpoznawaj ten zapis, ale w nowym kodzie używaj callback props.

<!-- WarriorCard.svelte - Svelte 4 (legacy) -->
<script>
  import { createEventDispatcher } from 'svelte';

  export let warrior;

  const dispatch = createEventDispatcher();
</script>

<button on:click={() => dispatch('select', warrior)}>Wybierz</button>
<!-- Parent.svelte - Svelte 4 (legacy) -->
<WarriorCard {warrior} on:select={(e) => selected = e.detail} />

Dane przychodziły w e.detail. Z callback props dostajesz je wprost jako argument funkcji.

Kod do tej lekcji: App.svelte
<script>
  // Callback props pass through components like any other prop
  import Battle from './Battle.svelte';

  let battleLog = $state([]);

  function handleAttack(entry) {
    battleLog = [...battleLog, entry].slice(-5);
  }
</script>

<main class="arena">
  <h1>Battle Arena</h1>
  <p>App passes onAttack to Battle, Battle passes it on to each Fighter</p>

  <Battle onAttack={handleAttack} />

  <div class="log">
    <h2>Battle log:</h2>
    {#each battleLog as entry}
      <p>{entry.attacker} dealt {entry.damage} damage!</p>
    {/each}
    {#if battleLog.length === 0}
      <p class="empty">No attacks...</p>
    {/if}
  </div>
</main>

<style>
  .arena {
    font-family: 'Georgia', serif;
    max-width: 600px;
    margin: 0 auto;
    padding: 2rem;
    background: #1a0f0a;
    color: #e8d5b7;
    min-height: 100vh;
  }

  h1 {
    color: #d4af37;
    text-align: center;
  }

  p {
    text-align: center;
    color: #8b7355;
  }

  .log {
    margin-top: 2rem;
    background: rgba(0,0,0,0.3);
    border-radius: 8px;
    padding: 1rem;
  }

  .log h2 {
    color: #d4af37;
    font-size: 1rem;
    margin-top: 0;
  }

  .log p {
    text-align: left;
    margin: 0.5rem 0;
    padding: 0.5rem;
    background: rgba(139, 0, 0, 0.2);
    border-radius: 4px;
  }

  .empty {
    color: #666;
    font-style: italic;
  }
</style>

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Jaki jest preferowany sposób wysyłania zdarzeń w Svelte 5?

Zadania praktyczne w grze

  • Edytor kodu

    Uzupełnij kod, aby kliknięcie przycisku wywołało funkcję onSelect

Przydatne artykuły