Cours Svelte et SvelteKit · Module 6: Logique des templates
{#key} et {@html}
Dans cette leçon4
{#key} - Forcer un nouveau rendu
<script>
let userId = $state(1);
</script>
<!-- Le composant sera détruit et recréé lorsque userId change -->
{#key userId}
<UserProfile id={userId} />
{/key}
<button onclick={() => userId++}>Next user</button>Utile pour :
- Réinitialiser l'état du composant
- Redéclencher des animations
- Forcer un nouveau fetch
{@html} - HTML brut
<script>
let sagaContent = $state('<strong>Ragnar</strong> set off on a <em>great expedition</em>.');
</script>
<!-- Affiche le HTML sans échappement -->
{@html sagaContent}ATTENTION : Utilise uniquement avec des données fiables ! Des attaques XSS sont possibles.
<script>
// DANGEREUX - données utilisateur
let userInput = '<img src="x" onerror="alert(1)">';
</script>
{@html userInput} <!-- NE FAIS PAS ÇA ! -->Cette image n'existe pas, donc le navigateur exécute le code de onerror : un attaquant lance son propre script sur ta page.
{@const} - Constantes locales
{#each warriors as warrior}
{@const fullTitle = `${warrior.rank} ${warrior.name}`}
{@const isElite = warrior.strength > 80}
<div class:elite={isElite}>
<h3>{fullTitle}</h3>
</div>
{/each}{@debug} - Débogage
<script>
let warriors = $state([
{ id: 1, name: 'Ragnar' },
{ id: 2, name: 'Bjorn' }
]);
let selectedId = $state(null);
</script>
<!-- Met en pause l'exécution lorsque les valeurs changent -->
{@debug warriors, selectedId}
{#each warriors as warrior}
<p>{warrior.name}</p>
{/each}Dans le script, la rune $inspect(warriors, selectedId) fonctionne de la même façon : elle affiche les valeurs dans la console à chaque changement.
Code de cette leçon : App.svelte
<script>
// {#await} - handling Promises
// $state: assigning a new Promise re-renders the {#await} block
let promise = $state(null);
function fetchWarrior() {
promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.3) {
resolve({
name: 'Ragnar Lothbrok',
title: 'King of Kattegat',
strength: 95,
wisdom: 88
});
} else {
reject(new Error('The warrior did not answer the call!'));
}
}, 1500);
});
}
function reset() {
promise = null;
}
</script>
<main class="async-demo">
<h1>⏳ {"{#await}"} - Async</h1>
<p class="subtitle">Handling Promises in templates</p>
<section>
<h2>Summon a warrior</h2>
<div class="buttons">
<button onclick={fetchWarrior}>Summon</button>
<button onclick={reset}>Reset</button>
</div>
<div class="result">
{#if promise === null}
<p class="idle">Click "Summon" to call a warrior</p>
{:else}
{#await promise}
<div class="loading">
<div class="spinner"></div>
<p>Warrior is arriving...</p>
</div>
{:then warrior}
<div class="success">
<h3>{warrior.name}</h3>
<p class="title">{warrior.title}</p>
<div class="stats">
<div class="stat">
<span>Strength</span>
<strong>{warrior.strength}</strong>
</div>
<div class="stat">
<span>Wisdom</span>
<strong>{warrior.wisdom}</strong>
</div>
</div>
</div>
{:catch error}
<div class="error">
<p>{error.message}</p>
<button onclick={fetchWarrior}>Try again</button>
</div>
{/await}
{/if}
</div>
</section>
<section class="syntax">
<h2>Syntax</h2>
<pre>{"{#await promise}"}
Loading...
{"{:then data}"}
{"{data}"}
{"{:catch error}"}
{"{error.message}"}
{"{/await}"}</pre>
</section>
</main>
<style>
.async-demo {
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;
}
.subtitle {
text-align: center;
color: #8b7355;
}
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;
}
.buttons {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
button {
padding: 0.5rem 1rem;
background: #3d2914;
color: #e8d5b7;
border: 1px solid #d4af37;
border-radius: 4px;
cursor: pointer;
}
.result {
min-height: 150px;
padding: 1rem;
background: rgba(0,0,0,0.3);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.idle {
color: #8b7355;
font-style: italic;
}
.loading {
text-align: center;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #3d2914;
border-top-color: #d4af37;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.success {
text-align: center;
width: 100%;
}
.success h3 {
color: #ffd700;
margin: 0;
}
.title {
color: #8b7355;
font-style: italic;
margin: 0.25rem 0;
}
.stats {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 1rem;
}
.stat {
text-align: center;
}
.stat span {
display: block;
font-size: 0.85rem;
color: #8b7355;
}
.stat strong {
font-size: 1.5rem;
color: #d4af37;
}
.error {
text-align: center;
color: #ff6b6b;
}
.syntax pre {
background: rgba(0,0,0,0.5);
padding: 1rem;
border-radius: 4px;
font-family: monospace;
font-size: 0.9rem;
overflow-x: auto;
}
</style>Entrées de la documentation utilisées dans cette leçon
Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Quand utiliser le bloc {#key} ?
2. Pourquoi {@html} est-il dangereux ?