Kurs Svelte i SvelteKit · Moduł 4: Props i zdarzenia
Events - Komunikacja w górę
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. 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