Since HTML can’t express logic such as conditionals and loops you would have to write something like this using JavaScript.
Example.htmlCopy
<div id="app"></div>
<script>
let appElement = document.querySelector('#app')
let user = {
loggedIn: false
}
function toggle() {
user.loggedIn = !user.loggedIn
updateUI()
}
function updateUI() {
let html
if (user.loggedIn) {
html = `<button>Log out</button>`
}
if (!user.loggedIn) {
html = `<button>Log in</button>`
}
appElement.innerHTML = html
appElement.querySelector('button').onclick = toggle
}
updateUI()
</script>
It doesn’t look bad but I think we can do better. This is the same example using an #if
block in Svelte.
App.svelteCopy
<script>
let user = {
loggedIn: false
}
function toggle() {
user.loggedIn = !user.loggedIn
}
</script>
{#if user.loggedIn}
<button on:click={toggle}>Log out</button>
{/if}
{#if !user.loggedIn}
<button on:click={toggle}>Log in</button>
{/if}
Awesome, right? I want to emphasize how close Svelte is to HTML and I hope you’re excited about it.
There’s more logic blocks like #if
, #each
, #await
, and #key
for you to play around with.
This is using JavaScript to loop over a list of items and render them.
Example.htmlCopy
<div id="app"></div>
<script>
let appElement = document.querySelector('#app')
let todos = [
{ id: 1, text: 'Todo 1', completed: true },
{ id: 2, text: 'Todo 2', completed: false },
{ id: 3, text: 'Todo 3', completed: false },
{ id: 4, text: 'Todo 4', completed: false },
]
let todosHtml = ''
for (let todo of todos) {
let checked = todo.completed ? 'checked' : null
todosHtml += `
<li data-id=${todo.id}>
<input ${checked} type="checkbox" />
<span>${todo.text}</span>
</li>
`
}
appElement.innerHTML = `<ul>${todosHtml}</ul>`
</script>
The same example using #each
in Svelte.
App.svelteCopy
<script>
let todos = [
{ id: 1, text: 'Todo 1', completed: true },
{ id: 2, text: 'Todo 2', completed: false },
{ id: 3, text: 'Todo 3', completed: false },
{ id: 4, text: 'Todo 4', completed: false },
]
</script>
<ul>
{#each todos as todo}
<li>
<input checked={todo.completed} type="checkbox" />
<span>{todo.text}</span>
</li>
{/each}
</ul>
You can destructure values from the item you’re iterating over, get the index, and provide a key so Svelte can keep track of changes. Avoid using the index as the key because it’s not guaranteed to be unique so use a unique value instead.
App.svelteCopy
<ul>
{#each todos as {id, text, completed}, index (id)}
<li>
<input checked={completed} type="checkbox" />
<span>{text}</span>
</li>
{/each}
</ul>
If you’re fetching data on the client this is how it would look using JavaScript.
Example.htmlCopy
<div id="app"></div>
<script>
let appElement = document.querySelector('#app')
async function fetchPokemon(pokemonName) {
let url = `https://pokeapi.co/api/v2/pokemon/`
let response = await fetch(`${url}${pokemonName}`)
let { name, sprites } = await response.json()
return {
name,
image: sprites['front_default']
}
}
async function renderUI() {
let { name, image } = await fetchPokemon('pikachu')
appElement.innerHTML = `
<h1>${name}</h1>
<img src=${image} alt=${name} />
`
}
renderUI()
</script>
In Svelte you can easily resolve a promise using the #await
block but you can also resolve the promise in the <script>
tag if you want .
App.svelteCopy
<script>
async function fetchPokemon(pokemonName) {
let url = `https://pokeapi.co/api/v2/pokemon/`
let response = await fetch(`${url}${pokemonName}`)
let { name, sprites } = await response.json()
return {
name,
image: sprites['front_default']
}
}
</script>
{#await fetchPokemon('pikachu')}
<p>Fetching Pokemon...</p>
{:then pokemon}
<h1>{pokemon.name}</h1>
<img src={pokemon.image} alt={pokemon.name} />
{:catch error}
<p>Something went wrong: {error.message}</p>
{/await}
In the JavaScript example we didn’t even add checks for scenarios where the promise could be pending, fulfilled, or rejected and just hope it works. 😬 Using Svelte you don’t have to think about it.
Leave a Reply