Vue and React and frameworks, Svelte is a compiler - Svelte compiles the code written in Svelte syntax, transforms it into compact vanilla JS, and ships that to the user, instead of shipping the code and the framework like with Vue/React.
Svelte translates instructions into efficient runtime code at build time.
As typically in JS, nobody uses the "$:" (like also "break") - a labeled statement, that are used in conjunction with loops and such. And because it's practically never used, Svelte takes advantage of this fact and hijacks the "$:".
It's used for dynamic calculations, where you want these calculations to be executed whenever some variable somewhere in the code changes, and re-do the calculation.
$: uppercaseName = name.toUpperCase()
$: console.log(name)
$: if (name=="Kenneth") {age=0}
To allow for a variable of a component to be set by a parent component, inside the component's svelte file, e.g. ContactCard.svelte, export the variables.
<script>
export let userName
export let userJobTitle
export let userDescription
export let userImage
</script>
Importing the child component in the parent component works as such:
<script>
import ContactCard from './ContactCard.svelte'
</script>
In the parent component, the child component together with its props is used like this:
<ContactCard
userName={name}
userJobTitle={jobTitle}
userDescription={description}
{userImage} // if the prop name and the variable name in the parent component have the same name, you can use this abbreviated syntax
/>
Instead of having to write a typical input field event listener function with cumbersome markup line like this:
<input type="text" value={name} on:input={nameInput}/>
And the "nameInput" function like this:
function nameInput(e) {
const enteredValue = e.target.value
name = enteredValue
}
It's possible in Svelte to use "bind:" syntax to create a bi-directional info flow in a much more concise way, without needing a separate event listener function:
<input type="text" bind:value={name}/>
Add a conditional css class based on whether userImage contains an address or not. If not, the "thumb-placeholder" is displayed instead.
<div class="thumb" class:thumb-placeholder={!userImage}>
Tasks
Add an input field that allows users to enter a course goal.
Output the user input in a h1 tag.
Color the output red (e.g. by adding a class) if it contains at least one exclamation mark.
Put the h1 tag + output into a separate component to which you pass the user input.
You can create conditional rendering in the html code by using this syntax:
{if formState=="invalid"}
<p>Invalid input</p>
{:else if formState=='none'}
<p>No contacts recorded, start adding contacts</p>
{:else}
<p>Please start adding contacts!</p>
Similar to the if, else, else-if conditional html rendering, it's possible to iterate through lists using each syntax to render html content:
{each createdContacts as contact, i (contact.id)}
<h2># {i + 1}</h2>
<ContactCard
userName={contact.name}
jobTitle={contact.title}
description={contact.desc}}
userImage={contact.image} />
{:else}
<p>Please start adding contacts</p>
{/each}
To improve performance when handling lists and removing elements, it's best to use unique identifiers for each entry in order not have svelte re-render all of the list items after the item in question. If the item has a unique identifier, only that item will be removed and no other items will be touched during re-rendering.
Add a password input field and save the user input in a variable.
Output "too short" if the password is shorter than 5 characters and "too long" if it's longer than 10.
Output the password in a paragraph tag if it's between these boundaries.
Add a button and let the user add the passwords to an array.
Output the array values (=passwords) in an unordered list (ul tag).
Bonus: if a password is clicked, remove it from the list.
Code:
<script>
let password = ''
let passwordList = []
function savePassword() {
passwordList = [...passwordList, {
id: Math.random(),
password
}]
console.log(JSON.stringify(passwordList));
}
function deletePassword(id) {
passwordList = passwordList.filter(passObj => passObj.id != id)
}
</script>
<div>
<input type='text' bind:value={password}/>
<button on:click={savePassword}>Save password</button>
{if password.length<5}
<p>Password too short!</p>
{:else if password.length>10}
<p>Password too long!</p>
{:else}
<p>Password: {password}</p>
{/if}
</div>
<div>
<ul>
{each passwordList as psw, i (psw.id)}
<li on:click={()=>{deletePassword(psw.id)}}>{psw.password}</li>
{/each}
</ul>
</div>
Possible to add a very short and convenient event modifier syntax, like this:
<button on:click|preventDefault={savePassword}>Submit password</button>
This on:click|preventDefault would prevent an automatic page reloading after clicking a button inside a form, for example. Normally this preventDefault would have to be performed in a separate manual function in the code:
function savePassword(e) {
e.preventDefault()
}
Or, for example, can use once modifier to make the button clickable only once:
<button on:click|once={savePassword}>Submit password</button>
If you omit writing an event handler for an on:click etc.m then you can forward the event to the parent component.
Instead of the on:click being handled in the Product.svelte component, it gets forwarded to the App.svelte component.
In Product.svelte
<button on:click > Add to Cart </button>
In the App.svelte
<Product on:click={() => alert('Clicked')}
However, if the on:click in the Product.svelte is omitted, then the even will not be forwarded.
If you have multiple buttons, then forwarding events to the parent component will not work that great, because then it'll be difficult to distinguish between which of the buttons was clicked when trying to resolve the on:click events. It would be possible to do that by dissecting the event.target object, but it's not really an elegant solution.
Instead, it's possible to create custom events by leveraging the createEventDispatcher feature of Svelte.
<script>
import {createEventDispatcher} from 'svelte'
const dispatch = createEventDispatcher()
</script>
dispatch takes in two arguments: 1) the custom function name, and 2) any data to be passed onto the function.
dispatch and its custom function can be called both inline, as well as separately in the