2020-04-23

Svelte Tutorial 6: Bindings (English & Indonesia)

English


Bindings

As a general rule, data flow in Svelte is top down — a parent component can set props on a child component, and a component can set attributes on an element, but not the other way around. Bindings is useful way in svelte to keep all value of variable or attribute that is bound together to stay the same. Here is type of bindings we can use.

Text Input


If we want to bind value of text input we can use something like <input bind:value={name}>. Below is some code example:

<script>
let name = 'world';
</script>

<input bind:value={name}>

<h1>Hello {name}!</h1>

Numeric Input


In the DOM, everything is a string. That's unhelpful when you're dealing with numeric inputs — type=number and type=range — as it means you have to remember to coerce input.value before using it.

With bind:value, Svelte takes care of it for you:

<script>
let a = 1;
let b = 2;
</script>

<label>
<input type=number bind:value={a} min=0 max=10>
<input type=range bind:value={a} min=0 max=10>
</label>

<label>
<input type=number bind:value={b} min=0 max=10>
<input type=range bind:value={b} min=0 max=10>
</label>

<p>{a} + {b} = {a + b}</p>

Checkbox Input


Checkboxes are used for toggling between states. Instead of binding to input.value, we bind to input.checked:

<input type=checkbox bind:checked={yes}>

Group Input


If you have multiple inputs relating to the same value, you can use bind:group along with the value attribute. Radio inputs in the same group are mutually exclusive; checkbox inputs in the same group form an array of selected values.

Add bind:group to each input. Here is some example:

<script>
let scoops = 1;
let flavours = ['Mint choc chip'];

let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];

function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')} and ${flavours[flavours.length - 1]}`;
}
</script>

<h2>Size</h2>

<label>
<input type=radio bind:group={scoops} value={1}>
One scoop
</label>

<label>
<input type=radio bind:group={scoops} value={2}>
Two scoops
</label>

<label>
<input type=radio bind:group={scoops} value={3}>
Three scoops
</label>

<h2>Flavours</h2>

{#each menu as flavour}
<label>
<input type=checkbox bind:group={flavours} value={flavour}>
{flavour}
</label>
{/each}

{#if flavours.length === 0}
<p>Please select at least one flavour</p>
{:else if flavours.length > scoops}
<p>Can't order more flavours than scoops!</p>
{:else}
<p>
You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'}
of {join(flavours)}
</p>
{/if}

Textarea Input


The <textarea> element behaves similarly to a text input in Svelte — use bind:value=:

<textarea bind:value={value}></textarea>

In cases like these, where the names match, we can also use a shorthand form:

<textarea bind:value></textarea>

This applies to all bindings, not just textareas.

Select Binding


We can also use bind:value= with <select> elements.

<script>
  let questions = [
    { id: 1, text: `Where did you go to school?` },
    { id: 2, text: `What is your mother's name?` },
    { id: 3, text: `What is another personal fact that an attacker could easily find with Google?` }
  ];
  let selected;
  let answer = '';
  function handleSubmit() {
    alert(`answered question ${selected.id} (${selected.text}) with "${answer}"`);
  }
</script>
<style>
  input { display: block; width: 500px; max-width: 100%; }
</style>

<h2>Insecurity questions</h2>

<form on:submit|preventDefault={handleSubmit}>
  <select bind:value={selected} on:change="{() => answer = ''}">
  <!-- ^^^^ select binding here ^^^^ -->
    {#each questions as question}
      <option value={question}>{question.text}</option>
    {/each}
  </select>
  <input bind:value={answer}>
  <button disabled={!answer} type=submit>Submit</button>
</form>

<p>selected question {selected ? selected.id : '[waiting...]'}</p>

Note that the <option> values are objects rather than strings. Svelte doesn't mind.

Because we haven't set an initial value of selected, the binding will set it to the default value (the first in the list) automatically. Be careful though — until the binding is initialised, selected remains undefined, so we can't blindly reference e.g. selected.id in the template.

Select Multiple


A select can have a multiple attribute, in which case it will populate an array rather than selecting a single value.

Returning to our ice cream example in group binding , we can replace the checkboxes with a <select multiple>:

<h2>Flavours</h2>

<select multiple bind:value={flavours}>
{#each menu as flavour}
<option value={flavour}>
{flavour}
</option>
{/each}
</select>

Contenteditable Binding


Elements with a contenteditable="true" attribute support bind:textContent and bind:innerHTML bindings:

<script>
  let html = '<p>Write some text!</p>';
</script>

<div
  contenteditable="true"
  bind:innerHTML={html}  
></div> <!-- ^ innerHTML binding -->

<pre>{html}</pre>
<style>
  [contenteditable] {
    padding: 0.5em;
    border: 1px solid #eee;
    border-radius: 4px;
  }
</style>

Without inner HTML bind in above example, changes in contenteditable wont automatically update text inside <pre> tag.

Each Block Binding


You can even bind to properties inside an each block.

<script>
  let todos = [
    { done: false, text: 'finish Svelte tutorial' },
    { done: false, text: 'build an app' },
    { done: false, text: 'world domination' }
  ];

  function add() {
    todos = todos.concat({ done: false, text: '' });
  }

  function clear() {
    todos = todos.filter(t => !t.done);
  }

$: remaining = todos.filter(t => !t.done).length;
</script>

<style> .done { opacity: 0.4; </style>

<h1>Todos</h1>
{#each todos as todo}
  <div class:done={todo.done}>
    <input
      type=checkbox
      bind:checked={todo.done}  <!-- each binding -->
    >
    <input
      placeholder="What needs to be done?"
      bind:value={todo.text}    <!-- each binding -->
    >
  </div>
{/each}

<p>{remaining} remaining</p>
<button on:click={add}> Add new </button>
<button on:click={clear}> Clear completed </button>

The point is in each-block binding, every member of array is bound with every component in each-block.

Media Element


The <audio> and <video> elements have several properties that you can bind to. This example demonstrates a few of them.

<video
  poster="https://sveltejs.github.io/assets/caminandes-llamigos.jpg"
  src="https://sveltejs.github.io/assets/caminandes-llamigos.mp4"
  on:mousemove={handleMousemove}
  on:mousedown={handleMousedown}
  bind:currentTime={time}
  bind:duration
  bind:paused
></video>

Remember that bind:duration is equivalent to bind:duration={duration}. Now, when you click on the video, it will update timeduration and paused as appropriate. This means we can use them to build custom controls.

The complete set of bindings for <audio> and <video> is as follows — six readonly bindings...
  • duration (readonly) — the total duration of the video, in seconds
  • buffered (readonly) — an array of {start, end} objects
  • seekable (readonly) — ditto
  • played (readonly) — ditto
  • seeking (readonly) — boolean
  • ended (readonly) — boolean
...and four two-way bindings:
  • currentTime — the current point in the video, in seconds
  • playbackRate — how fast to play the video, where 1 is 'normal'
  • paused — this one should be self-explanatory
  • volume — a value between 0 and 1
Videos additionally have readonly videoWidth and videoHeight bindings. For complete interactive example of Media-Element binding you can check it here.

Dimensions


Every block-level element has clientWidthclientHeightoffsetWidth and offsetHeight bindings:

<script>
  let w;
  let h;
  let size = 42;
  let text = 'edit me';
</script>
<style>
  input { display: block; }
  div { display: inline-block; }
  span { word-break: break-all; }
</style>

<input type=range bind:value={size}>
<input bind:value={text}>
<p>size: {w}px x {h}px</p>

<div bind:clientWidth={w} bind:clientHeight={h}>
  <span style="font-size: {size}px">{text}</span>
</div>

In the above example when we slide the slider it's automatically variable size, because we already bind the value with size variable. Then when size of text change, width and height of div element also automatically change, and bind with variable w and h respectively, so the text <p>size: {w}px x {h}px</p> also got updated.


This


The readonly this binding applies to every element (and component) and allows you to obtain a reference to rendered elements. For example, we can get a reference to a <canvas> element:

<canvas
  bind:this={canvas}
  width={32}
  height={32}
></canvas>

Note that the value of canvas will be undefined until the component has mounted, so we put the logic inside the onMount life cycle function. You can check complete interactive example here.

Component Bindings


Just as you can bind to properties of DOM elements, you can bind to component props. For example, we can bind to the value prop of this <Keypad> custom component as though it were a form element:

<Keypad bind:value={pin} on:submit={handleSubmit}/>

Now, when the user interacts with the keypad, the value of pin in the parent component is immediately updated. Check complete example of app to enter pin number and alert the result here.
Don't forget to update keypad component with bind:value={pin} to see the correct result.

Use component bindings sparingly. It can be difficult to track the flow of data around your application if you have too many of them, especially if there is no 'single source of truth'.




Indonesia


Bindings


Aturan umum di Svelte adalah data flow akan terjadi secara top down — komponen parent bisa mengatur properti di komponen child, dan sebuah komponen bisa mengatur atribut dalam suatu elemen, tapi TIDAK sebaliknya. Binding adalah suatu cara yang berguna untuk memastikan semua nilai variabel atau atribut terikat untuk selalu sama. Berikut adalah tipe-tipe binding yang bisa kita gunakan.

Text Input


Jika kita ingin melakukan bind pada isi dari input teks kita bisa menggunakan seperti ini <input bind:value={name}>. Berikut adalah contoh program:

<script>
let name = 'world';
</script>

<input bind:value={name}>

<h1>Hello {name}!</h1>

Numeric Input


Pada DOM, semua dianggap string, hal ini merpersulit jika kita berurusan dengan input numerik — type=number dan type=range — karena ini berarti kita harus mengolah  input.value sebelum bisa menggunakannya.

Dengan bind:value, Svelte yang akan menyelesaikan urusan itu:

<script>
let a = 1;
let b = 2;
</script>

<label>
<input type=number bind:value={a} min=0 max=10>
<input type=range bind:value={a} min=0 max=10>
</label>

<label>
<input type=number bind:value={b} min=0 max=10>
<input type=range bind:value={b} min=0 max=10>
</label>

<p>{a} + {b} = {a + b}</p>

Checkbox Input


Checkbox digunakan untuk melakukan toggle antara true atau false. Untuk melakukan binding pada checkbox kita tidak menggunakan input.value, tetapi kita melakukan bind ke input.checked:

<input type=checkbox bind:checked={yes}>

Group Input


Jika kita memiliki beberapa input yang berhubungan dengan nilai yang sama, kita bisa menggunakan bind:group bersamaan dengan atribut value. Input radio pada grup yang sama tidak bisa dipilih bersamaan (mutually exclusive); sementara input checkbox pada grup yang sama akan membentuk array dari nilai-nilai yang dipilih.

Tambahkan bind:group pada masing-masing input. Berikut adalah contohnya:

<script>
let scoops = 1;
let flavours = ['Mint choc chip'];

let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];

function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')} and ${flavours[flavours.length - 1]}`;
}
</script>

<h2>Size</h2>

<label>
<input type=radio bind:group={scoops} value={1}>
One scoop
</label>

<label>
<input type=radio bind:group={scoops} value={2}>
Two scoops
</label>

<label>
<input type=radio bind:group={scoops} value={3}>
Three scoops
</label>

<h2>Flavours</h2>

{#each menu as flavour}
<label>
<input type=checkbox bind:group={flavours} value={flavour}>
{flavour}
</label>
{/each}

{#if flavours.length === 0}
<p>Please select at least one flavour</p>
{:else if flavours.length > scoops}
<p>Can't order more flavours than scoops!</p>
{:else}
<p>
You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'}
of {join(flavours)}
</p>
{/if}

Textarea Input


Elemen <textarea> mempunya sifat mirip dengan input teks — gunakan bind:value= seperti berikut:

<textarea bind:value={value}></textarea>

Pada kasus seperti ini dimana nama variabel sama dengan nama atribut kita bisa menuliskan dengan cara singkat:

<textarea bind:value></textarea>

Cara menyingkat ini berlaku pada semua elemen bukan textarea saja.

Select Binding


Kita juga bisa menggunakan bind:value= dengan elemen <select> .

<script>
  let questions = [
    { id: 1, text: `Where did you go to school?` },
    { id: 2, text: `What is your mother's name?` },
    { id: 3, text: `What is another personal fact that an attacker could easily find with Google?` }
  ];
  let selected;
  let answer = '';
  function handleSubmit() {
    alert(`answered question ${selected.id} (${selected.text}) with "${answer}"`);
  }
</script>
<style>
  input { display: block; width: 500px; max-width: 100%; }
</style>

<h2>Insecurity questions</h2>

<form on:submit|preventDefault={handleSubmit}>
  <select bind:value={selected} on:change="{() => answer = ''}">
  <!-- ^^^^ select binding here ^^^^ -->
    {#each questions as question}
      <option value={question}>{question.text}</option>
    {/each}
  </select>
  <input bind:value={answer}>
  <button disabled={!answer} type=submit>Submit</button>
</form>

<p>selected question {selected ? selected.id : '[waiting...]'}</p>

Perhatikan bahwa nilai dari  <option> merupakan object dan bukan string. Svelte bisa melakukan ini secara otomatis.

Karena kita belum melakukan pengaturan nilai awal dari selected, proses binding akan memberikan nilai default (yang pertama yang ada di daftar) secara otomatis. Akan tetapi kita harus berhati-hati — sampai proses binding dijalankan, selected akan tetap bernilai undefined, jadi kita tidak bisa menggunakan contohnya selected.id pada template.

Select Multiple


Sebuah elemen select bisa memiliki atribut multiple, di mana pada kasus ini, nilainya akan tersimpan pada array, dan bukan satu variabel saja.

Kembali pada contoh app ice cream pada binding group, kita bisa menggantik checkbox dengan <select multiple>:

<h2>Flavours</h2>

<select multiple bind:value={flavours}>
{#each menu as flavour}
<option value={flavour}>
{flavour}
</option>
{/each}
</select>

Contenteditable Binding


Elemen dengan atribut contenteditable="true" mendukung binding bind:textContent dan bind:innerHTML :

<script>
  let html = '<p>Write some text!</p>';
</script>

<div
  contenteditable="true"
  bind:innerHTML={html}  
></div> <!-- ^ innerHTML binding -->

<pre>{html}</pre>
<style>
  [contenteditable] {
    padding: 0.5em;
    border: 1px solid #eee;
    border-radius: 4px;
  }
</style>

Pada contoh di atas, tanpa menggunakan bind, perubahan di contenteditable tidak akan meng update teks di dalam tag <pre> secara otomatis.

Each Block Binding


Kita juga bisa melakukan bind pada property di dalam blok each.

<script>
  let todos = [
    { done: false, text: 'finish Svelte tutorial' },
    { done: false, text: 'build an app' },
    { done: false, text: 'world domination' }
  ];

  function add() {
    todos = todos.concat({ done: false, text: '' });
  }

  function clear() {
    todos = todos.filter(t => !t.done);
  }

$: remaining = todos.filter(t => !t.done).length;
</script>

<style> .done { opacity: 0.4; </style>

<h1>Todos</h1>
{#each todos as todo}
  <div class:done={todo.done}>
    <input
      type=checkbox
      bind:checked={todo.done}  <!-- each binding -->
    >
    <input
      placeholder="What needs to be done?"
      bind:value={todo.text}    <!-- each binding -->
    >
  </div>
{/each}

<p>{remaining} remaining</p>
<button on:click={add}> Add new </button>
<button on:click={clear}> Clear completed </button>

Hal utama yang terjadi pada binding di blok each, setiap anggota array akan terikat dengan setiap komponen di dalam blok each.

Media Element


Elemen <audio> dan <video> mempunyai beberapa elemen yang bisa kita bind. Berikut adalah beberapa contoh nya.

<video
  poster="https://sveltejs.github.io/assets/caminandes-llamigos.jpg"
  src="https://sveltejs.github.io/assets/caminandes-llamigos.mp4"
  on:mousemove={handleMousemove}
  on:mousedown={handleMousedown}
  bind:currentTime={time}
  bind:duration
  bind:paused
></video>

Jangan lupa bahwa bind:duration sama dengan bind:duration={duration}. Sekarang, ketika kita mengklik video, kita akan mengupdate timeduration dan paused sesuai yang kita pilih. Kita juga bisa memanfaatkan bind untuk membuat custom control untuk video.

Daftar binding lengkap untuk <audio> dan <video> adalah sebagai berikut — 6 binding readonly...
  • duration (readonly) — durasi total video dalam detik
  • buffered (readonly) — array dari object {start, end}
  • seekable (readonly) — ditto
  • played (readonly) — ditto
  • seeking (readonly) — boolean
  • ended (readonly) — boolean

...dan empat binding 2 arah:
  • currentTime — titik waktu pada video, dalam detik
  • playbackRate — kecepatan menjalankan video,  1 adalah 'normal'
  • paused — sedang terpause atau tidak
  • volume — bernilai antara 0 sampai 1

Video mempunyai tambahan readonly binding videoWidth dan videoHeight. Untuk contoh interaktif Elemen Media yang lebih lengkap bisa dilihat di sini.

Dimensions


Setiap elemen blok memiliki binding clientWidthclientHeightoffsetWidth dan offsetHeight:

<script>
  let w;
  let h;
  let size = 42;
  let text = 'edit me';
</script>
<style>
  input { display: block; }
  div { display: inline-block; }
  span { word-break: break-all; }
</style>

<input type=range bind:value={size}>
<input bind:value={text}>
<p>size: {w}px x {h}px</p>

<div bind:clientWidth={w} bind:clientHeight={h}>
  <span style="font-size: {size}px">{text}</span>
</div>

Pada contoh di atas saat kita menggeser slider maka nilai variabel akan otomatis berubah, karena kita sudah melakukan bind value dengan variabel size. Kemudian ketika ukuran tulisan berubah, lebar dan tinggi dari elemen div juga akan otomatis berubah, dan berlanjut dengan bind ke variabel w and h , maka teks <p>size: {w}px x {h}px</p> juga ikut terupdate.

This


Binding readonly this berlaku pada semua elemen (dan komponen) dan memungkinkan kita untuk mendapatkan referensi untuk elemen yang sedang dirender. Sebagai contoh, kita bisa mendapatkan referensi untuk elemen <canvas>:

<canvas
  bind:this={canvas}
  width={32}
  height={32}
></canvas>

Perhatikan bahwa nilai dari canvas akan tetap undefined sampai komponen sudah ter mount, maka kita meletakkan proses logika di dalam siklus fungsi life cycle onMount . Contoh interaktif lengkap bisa dilihat di sini.

Component Bindings


Sama halnya dengan kita bisa melakukan bind pada properti elemen DOM, kita juga bisa melakukan bind pada properti komponen. Contohnya kita bisa melakukan bind pada properti  value pada komponen custom <Keypad> seperti pada elemen form:

<Keypad bind:value={pin} on:submit={handleSubmit}/>

Sekarang, ketika user berinteraksi dengan keypad, maka nilai dari pin pada komponen parentakan langsung terupdate. Contoh interaktif lengkap bisa dilihat di sini.

Usahakan tidak menggunakan binding komponen terlalu sering, karena bisa mengakibatkan program sulit untuk dipaham jika terlalu banyak, terutama jika tidak ada satu sumber data yang pasti.

Svelte Tutorial 5: Events (English & Indonesia)

English


Events


As we've briefly seen already, you can listen to any event on an element with the on: directive:

<script>
let m = { x: 0, y: 0 };

function handleMousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
</script>

<style>
div { width: 100%; height: 100%; }
</style>

<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>

See this list for list of all events available on the DOM.

Inline Handlers


You can also declare event handlers inline:

<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>

The quote marks are optional, but they're helpful for syntax highlighting in some environments.

Event Modifiers


DOM event handlers can have modifiers that alter their behaviour. For example, a handler with a once modifier will only run a single time:

<script>
function handleClick(e) { // you can access the event and event.target
alert('no more alerts')
}
</script>

<button on:click|once={handleClick}>
Click me
</button>

The full list of modifiers:
  • preventDefault — calls event.preventDefault() before running the handler. Useful for client-side form handling.
  • stopPropagation — calls event.stopPropagation(), preventing the event reaching the next element
  • passive — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
  • capture — fires the handler during the capture phase instead of the bubbling phase
  • once — remove the handler after the first time it runs
  • self — only trigger handler if event.target is the element itself
You can chain modifiers together, e.g. on:click|once|capture={...}.

Component Events


Components can also dispatch events. To do so, they must create an event dispatcher. This event is custom made by you, so you can use any name for event name, in this example we use 'message' for event name. createEventDispatcher must be called when the component is first instantiated — you can't do it later inside e.g. a setTimeout callback. This links dispatch to the component instance.

<script>
 // Inner.svelte

import { createEventDispatcher } from 'svelte';

const dispatch = createEventDispatcher();

function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>

After that we can use the component in our main app. We can alert text we got from component, that is contained in event.detail. Notice the event name in Inner component is 'message', you can use any name it's not predefined name.

<script>
import Inner from './Inner.svelte';

function handleMessage(event) {
alert(event.detail.text); // Hello! from Inner.svelte
}
</script>

<Inner on:message={handleMessage}/>

Event Forwarding


Unlike DOM events, component events don't bubble. If you want to listen to an event on some deeply nested component, the intermediate components must forward the event. For example we have App.svelte for main app, Inner.svelte and Outer.svelte for component will be used in main app. Outer will contain Inner component (App <- Outer <- Inner). When Inner dispatch event, event cannot be used in main app, unless we do event forwarding in Outer component. This is example of event forwarding in Outer.svelte. Notice the <Inner on:message/> instead of just <Inner/>

<script>
import Inner from './Inner.svelte';
</script>

<Inner on:message/>

DOM Event Forwarding


Event forwarding works for DOM events too. For example if we made custom button like this

<style>
button {
height: 4rem;
width: 8rem;
background-color: #aaa;
border-color: #f1c40f;
color: #f1c40f;
font-size: 1.25rem;
background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
background-position: 100%;
background-size: 400%;
transition: background 300ms ease-in-out;
}
button:hover {
background-position: 0;
color: #aaa;
}
</style>

<button>Click me</button>

Then we use custom button in main app

<script>
import CustomButton from './CustomButton.svelte';

function handleClick() {
alert('clicked');
}
</script>

<CustomButton on:click={handleClick}/>

This wont work, as you notice in CustomButton.svelte we haven't added event forwarding yet, so instead of using just  <button>Click me</button>, we need to type <button on:click>Click me</button> as event forwarding.




Indonesia


Events


Seperti yang sudah kita lihat sebelumnya, kita bisa menangkap even apapun di elemen apapun dengan menggunakan direktif on: :

<script>
let m = { x: 0, y: 0 };

function handleMousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
</script>

<style>
div { width: 100%; height: 100%; }
</style>

<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>

Lihat daftar ini untuk mengetahui semua even yang ada pada DOM.

Inline Handlers


Kita juga bisa deklarasi event handler langsung di dalam tag:

<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>

Tanda petik dua opsional, tapi bisa berguna untuk tampilan sintaks pada environment tertentu.

Event Modifiers


Event handler DOM bisa memiliki modifier yang mengubah kelakuan mereka, contoh nya handler dengan modifier once hanya akan dijalankan sekali saja:

<script>
function handleClick(e) { // you can access the event and event.target
alert('no more alerts')
}
</script>

<button on:click|once={handleClick}>
Click me
</button>

Daftar lengkap modifier:
  • preventDefault — memanggil event.preventDefault() sebelum menjalankan handler, berguna untuk pengaturan form di sisi client.
  • stopPropagation — memanggil event.stopPropagation(), mencegah event mencapai elemen berikutnya.
  • passive — meningkatkan performa scroll pada event touchpad/wheel mouse (svelte akan menambahkan secara otomatis ketika dirasa aman).
  • capture — memicu handler selama fase capture daripada fase bubble.
  • once — menghilangkan handler sesudah dijalankan satu kali.
  • self — hanya menjalankan handler jika  only trigger handler if event.target adalah element itu sendiri
Kita bisa menggabungkan beberapa modifiers sekaligus, contohnya on:click|once|capture={...}.

Component Events


Komponen juga bisa melakukan dispatch even. Untuk melakukan nya, komponen harus dibuatkan event dispatcher. Kita harus membuat even ini sendiri, jadi kita bisa menggunakan nama apapun untuk even nya, pada contoh berikut ini kita akan menggunakan nama even 'message'createEventDispatcher harus dijalankan ketika komponen pertama dibentuk --- kita tidak bisa melakukan belakangan, contoh di dalam callback  setTimeout.  createEventDispatcher akan menyambungkan dispatch ke komponen yang terbentuk.

<script>
 // Inner.svelte

import { createEventDispatcher } from 'svelte';

const dispatch = createEventDispatcher();

function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>

Sesudah itu kita bisa menggunakan komponen di app utama. Kita bisa melakukan alert teks yang kita dapatkan dari komponen, yang tersimpan dalam event.detail. Perhatikan nama even yang kita pakai di komponen Inner adalah 'message', kita bisa menggunakan nama yang lain.

<script>
import Inner from './Inner.svelte';

function handleMessage(event) {
alert(event.detail.text); // Hello! from Inner.svelte
}
</script>

<Inner on:message={handleMessage}/>

Event Forwarding


Tidak seperti even DOM, even komponen tidak bersifat bubble. Jika kita ingin menangkap suatu even yang terjadi dalam komponen di dalam komponen dst, komponen lain yang menjadi komponen perantaranya harus mem forward even tersebut. Sebagai contoh jika kita mempunyai App.svelte untuk app utama, Inner.svelte dan Outer.svelte adalah komponen yang akan digunakan di app utama. Outer akan mengandung komponen Inner  (App <- Outer <- Inner). Ketika Inner dispatch even, even itu tidak bisa digunakan di app utama, kecuali kita melakukan forward even yang di komponen Outer. Di bawah ini adalah contoh forward even di Outer.svelte. Perhatikan bagian <Inner on:message/> dan BUKAN <Inner/>

<script>
import Inner from './Inner.svelte';
</script>

<Inner on:message/>

DOM Event Forwarding


Forward even juga bisa digunakan untuk even DOM juga. Sebagai contoh jika kita membuat button custom seperti berikut:

<style>
button {
height: 4rem;
width: 8rem;
background-color: #aaa;
border-color: #f1c40f;
color: #f1c40f;
font-size: 1.25rem;
background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
background-position: 100%;
background-size: 400%;
transition: background 300ms ease-in-out;
}
button:hover {
background-position: 0;
color: #aaa;
}
</style>

<button>Click me</button>

Kemudian kita gunakan button itu di app utama:

<script>
import CustomButton from './CustomButton.svelte';

function handleClick() {
alert('clicked');
}
</script>

<CustomButton on:click={handleClick}/>

Program ini tidak akan berjalan, seperti yang kita lihat di CustomButton.svelte kita belum menambahkan even forwarding, jadi daripada menggunakan  <button>Click me</button>, kita harus menggunakan <button on:click>Click me</button> untuk menjalankan event forwarding.


Svelte Tutorial 4: Logic (Branch, Loop, and Async) (English & Indonesia)

English


Svelte offer ways to express logic, which HTML doesn't. Rule of the thumb is
{# to start logic block (ifeachawait)
{: for continuation logic block (elsethencatch)
{/ to close logic block

If Blocks (decision/branch)


We use tag {#if condition}{/if}  to do logical if. Here is some code example:

<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}

Else


Use {:else} to do logical else. If else block will look like this

{#if [condition]}
  ...
{:else}
  ...
{/if}

# character always indicates a block opening tag. A / character always indicates a block closing tag. A : character, as in {:else}, indicates a block continuation tag.

Else if


You can chain multiple condition using else if like this:

{#if [condition]}
  ...
{:else if [condition]}
  ...
{:else}
  ...
{/if}

Each  (looping)


If you need to loop over lists of data, use an each block:

<script>
let cats = [
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
];
</script>

<h1>The Famous Cats of YouTube</h1>
<ul>
{#each cats as cat}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{cat.name}
</a></li>
{/each}
</ul>

You can get the current index as a second argument, like so:

{#each cats as cat, i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{i + 1}: {cat.name}
</a></li>
{/each}

If you prefer, you can use destructuring — each cats as { id, name } — and replace cat.id and cat.name with id and name.

{#each cats as { id, name }, i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
{i + 1}: {name}
</a></li>
{/each}

Keyed each


When you add or remove something from each block, it will remove or add at the last position of data. But sometimes it's not what we want, for example when you use array operation slice(1), you want to remove first data without re-rendering/reset the rest. So in order to match the perspective we use keyed each to mark each generated object as unique like this:

{#each things as thing (thing.id)}
<Thing current={thing.color}/>
{/each}

instead of:

{#each things as thing}
<Thing current={thing.color}/>
{/each}

so when we remove first position of array, the component that is removed in the each block is also the first one not the last one, for interactive example you can check this https://svelte.dev/tutorial/keyed-each-blocks

Await Block (async calls)


Most web applications have to deal with asynchronous data at some point. Svelte makes it easy to await the value of  directly in your markup. Here is the example of await block:

{#await promise}
<p>...waiting</p>
{:then number}
<p>The number is {number}</p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}

For example:

<script>
  let promise = exampleAsync();
  async function exampleAsnyc() {
    await new Promise(r => setTimeout(r, 2000)); // eg. hitting certain API
    return Math.random()

  }
  function onClick() { promise = exampleAsync(); }
</script>
<button on:click="onClick">Click Me!</button>





Indonesia


Svelte memberikan jalan untuk melakukan operasi-operasi logika, yang tidak ada di dalam HTML. Aturan sintaks yang perlu dipegang adalah sebagai berikut:
{# untuk memulai blok logika (ifeachawait)
{: untuk melakukan sambungan dengan blok logika di atasnya (elsethencatch)
{/ untuk menutup blok logika

If Blocks (decision/branch)


Kita menggunakan {#if condition}{/if}  untuk melakukan logika if. Di bawah ini adalah contohnya:

<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}

Else


Gunakan {:else} untuk melakukan logika else. Blok else bisa dilakukan seperti ini:
{#if [condition]}
  ...
{:else}
  ...
{/if}

Karakter # akan mengindikasikan tag pembukaan blok. Karakter / akan selalu mengindikasikan tag penutupan. Karakter : seperti di {:else}, mengindikasikan tag sambungan blok.

Else if


Kita bisa melakukan kondisi berantai dengan else if seperti ini:

{#if [condition]}
  ...
{:else if [condition]}
  ...
{:else}
  ...
{/if}

Each  (looping)


Jika ingin melakukan loop pada sekumpulan data, gunakan each:

<script>
let cats = [
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
];
</script>

<h1>The Famous Cats of YouTube</h1>
<ul>
{#each cats as cat}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{cat.name}
</a></li>
{/each}
</ul>

Kita bisa mendapatkan indeks data sebagai argumen kedua, seperti ini

{#each cats as cat, i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{i + 1}: {cat.name}
</a></li>
{/each}

Bisa juga menggunakan destructuring— each cats as { id, name } — untuk menggantikan cat.id dan cat.name dengan id dan name.

{#each cats as { id, name }, i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
{i + 1}: {name}
</a></li>
{/each}

Keyed each


Ketika kita menambahkan atau menghapus komponen dari blok each, maka yang akan dirubah adalah komponen pada posisi paling akhir/bawah. Tapi kadang itu tidak sesuai dengan apa yang kita mau, contohnya jika kita menggunakan operasi array slice(1), kita ingin menghilangkan data yang paling atas/pertama, tanpa perlu mengatur ulang data yang tersisa. Maka untuk menyamakan perspektif kita bisa menggunakan keyed each untuk menandai masing-masing komponen yang ada.

{#each things as thing (thing.id)}
<Thing current={thing.color}/>
{/each}

dan bukan seperti ini:

{#each things as thing}
<Thing current={thing.color}/>
{/each}

jadi ketika kita menghapus array posisi pertama, maka komponen yang akan dihilangkan juga yang pertama bukan yang terakhir, untuk contoh interaktif bisa dilihat di https://svelte.dev/tutorial/keyed-each-blocks.

Await Block (async calls)


Kebanyakan aplikasi web harus berurusan dengan  asynchronous data pada suatu titik. Svelte membuat urusan ini menjadi mudah untuk menunggu data secara langsung pada markup HTML kita. Di bawah ini adalah contoh dari blok await:

{#await promise}
<p>...waiting</p>
{:then number}
<p>The number is {number}</p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}

Begini contoh cara pakainya:

<script>
  let promise = exampleAsync();
  async function exampleAsnyc() {
    await new Promise(r => setTimeout(r, 2000)); // eg. hitting certain API
    return Math.random()

  }
  function onClick() { promise = exampleAsync(); }
</script>

<button on:click="onClick">Click Me!</button>