Kurs Svelte i SvelteKit · Moduł 6: Logika szablonów
Praktyczne wzorce
W tej lekcji3
Filtrowanie i sortowanie
<script>
let warriors = $state([
{ id: 1, name: 'Ragnar', strength: 95 },
{ id: 2, name: 'Bjorn', strength: 88 },
{ id: 3, name: 'Ivar', strength: 70 }
]);
let filter = $state('');
let sortBy = $state('name');
let filtered = $derived(
warriors
.filter(w => w.name.toLowerCase().includes(filter.toLowerCase()))
.sort((a, b) => {
if (sortBy === 'name') return a.name.localeCompare(b.name);
return b.strength - a.strength;
})
);
</script>
<input bind:value={filter} placeholder="Szukaj..." />
<select bind:value={sortBy}>
<option value="name">Po imieniu</option>
<option value="strength">Po sile</option>
</select>
{#each filtered as warrior (warrior.id)}
<WarriorCard {warrior} />
{:else}
<p>Nie znaleziono wojowników</p>
{/each}Zagnieżdżone pętle
<script>
let ships = $state([
{
name: 'Drakkar Północy',
crew: ['Ragnar', 'Bjorn', 'Floki']
},
{
name: 'Morski Wilk',
crew: ['Ivar', 'Ubbe']
}
]);
</script>
{#each ships as ship}
<div class="ship">
<h3>{ship.name}</h3>
<ul>
{#each ship.crew as member}
<li>{member}</li>
{/each}
</ul>
</div>
{/each}Warunkowe atrybuty
<script>
let isActive = $state(true);
let isDisabled = $state(false);
let isHighlighted = $state(false);
let customClass = $state('');
</script>
<!-- Atrybut dodany tylko gdy true -->
<button disabled={isDisabled}>Kliknij</button>
<!-- Class dodana tylko gdy niepusta -->
<div class={customClass || undefined}>Content</div>
<!-- Wiele klas warunkowo: obiekt (Svelte 5.16+) -->
<div class={{ active: isActive, disabled: isDisabled, highlighted: isHighlighted }}>
Element
</div>
<!-- To samo dyrektywami class: (starszy zapis) -->
<div
class:active={isActive}
class:disabled={isDisabled}
class:highlighted={isHighlighted}
>
Element
</div>Od Svelte 5.16 atrybut class przyjmuje obiekt: klucz to nazwa klasy, a wartość decyduje, czy klasa zostaje. Dyrektywa class: nadal działa, ale w nowym kodzie wygodniejszy jest obiekt.
Kod do tej lekcji: App.svelte
<script>
// {#key} - forcing re-render
// {@html} - raw HTML
// {@const} - local constants
let count = $state(0);
let refreshKey = $state(0);
let items = $state([
{ id: 1, name: 'Sword', rarity: 'common', description: '<strong>Common sword</strong> of a viking' },
{ id: 2, name: 'Thor\'s Axe', rarity: 'legendary', description: '<em>Legendary</em> axe with <span style="color: #ffd700">lightning</span>' },
{ id: 3, name: 'Shield', rarity: 'rare', description: 'Reinforced with <u>runes</u>' }
]);
function refresh() {
refreshKey++;
}
const rarityColors = {
common: '#aaa',
rare: '#4a90d9',
legendary: '#ffd700'
};
</script>
<main class="special-blocks">
<h1>Special blocks</h1>
<section>
<h2>{"{#key}"} - forcing re-render</h2>
<p>Count: {count}</p>
<button onclick={() => count++}>+1</button>
<button onclick={refresh}>Refresh key: {refreshKey}</button>
{#key refreshKey}
<div class="animated-box">
This animation will trigger when the key changes
</div>
{/key}
</section>
<section>
<h2>{"{@html}"} - raw HTML</h2>
<p class="warning">Use carefully - can be dangerous!</p>
{#each items as item}
{@const color = rarityColors[item.rarity]}
<div class="item" style="border-color: {color}">
<h3 style="color: {color}">{item.name}</h3>
<p>{@html item.description}</p>
</div>
{/each}
</section>
<section>
<h2>{"{@const}"} - local constants in blocks</h2>
{#each items as item}
{@const isLegendary = item.rarity === 'legendary'}
{@const stars = isLegendary ? '***' : item.rarity === 'rare' ? '**' : '*'}
<div class="item-simple">
<span>{item.name}</span>
<span>{stars}</span>
</div>
{/each}
</section>
</main>
<style>
.special-blocks {
font-family: 'Georgia', serif;
max-width: 500px;
margin: 0 auto;
padding: 2rem;
background: #1a0f0a;
color: #e8d5b7;
min-height: 100vh;
}
h1 {
color: #d4af37;
text-align: center;
}
section {
background: rgba(212, 175, 55, 0.1);
border: 1px solid #d4af37;
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
h2 {
color: #d4af37;
font-size: 1rem;
margin-top: 0;
}
button {
padding: 0.5rem 1rem;
background: #3d2914;
color: #e8d5b7;
border: 1px solid #d4af37;
border-radius: 4px;
cursor: pointer;
margin-right: 0.5rem;
}
.animated-box {
margin-top: 1rem;
padding: 1rem;
background: rgba(212, 175, 55, 0.2);
border-radius: 4px;
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.warning {
background: rgba(255, 107, 107, 0.2);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.9rem;
}
.item {
padding: 0.75rem;
background: rgba(0,0,0,0.3);
border-left: 3px solid;
margin: 0.5rem 0;
border-radius: 0 4px 4px 0;
}
.item h3 {
margin: 0 0 0.25rem 0;
font-size: 1rem;
}
.item p {
margin: 0;
font-size: 0.9rem;
}
.item-simple {
display: flex;
justify-content: space-between;
padding: 0.5rem;
background: rgba(0,0,0,0.3);
margin: 0.25rem 0;
border-radius: 4px;
}
</style>Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?