2020-04-28

Svelte Tutorial 16: Special Elements (English & Indonesia)

English


<svelte:self>


Svelte provides a variety of built-in elements. The first, <svelte:self>, allows a component to contain itself recursively.

It's useful for things like this folder tree view, where folders can contain other folders. In Folder.svelte we want to be able to do this...

{#if file.type === 'folder'}
  <Folder {...file}/>
{:else}
  <File {...file}/>
{/if}

...but that's impossible, because a file can't import itself. Instead, we use <svelte:self>:

{#if file.type === 'folder'}
  <svelte:self {...file}/>
{:else}
  <File {...file}/>
{/if}

For full interactive example, you can check here.

<svelte:component>


A component can change its category altogether with <svelte:component>. Instead of a sequence of if blocks...

{#if selected.color === 'red'} <RedThing/>
{:else if selected.color === 'green'} <GreenThing/>
{:else if selected.color === 'blue'} <BlueThing/>
{/if}

...we can have a single dynamic component:

<svelte:component this={selected.component}/>

The this value can be any component constructor, or a falsy value — if it's falsy, no component is rendered. Here is full example:

<script>
  import RedThing from './RedThing.svelte';
  import GreenThing from './GreenThing.svelte';
  import BlueThing from './BlueThing.svelte';

  const options = [
    { color: 'red',   component: RedThing   },
    { color: 'green', component: GreenThing },
    { color: 'blue',  component: BlueThing  },
  ];

  let selected = options[0];
</script>

<select bind:value={selected}>
  {#each options as option}
    <option value={option}>{option.color}</option>
  {/each}
</select>

<svelte:component this={selected.component}/>

This is example of component RedThing.svelte:

<style>
  strong { color: red; }
</style>

<strong>red thing</strong>

<svelte:window>


Just as you can add event listeners to any DOM element, you can add event listeners to the window object with <svelte:window>.

<script>
  let key;
  let keyCode;

  function handleKeydown(event) {
    key = event.key;
    keyCode = event.keyCode;
  }
</script>

<style>
  div {
    display: flex;
    height: 100%;
    align-items: center;
    justify-content: center;
    flex-direction: column;
  }

  kbd {
    background-color: #eee;
    border-radius: 4px;
    font-size: 6em;
    padding: 0.2em 0.5em;
    border-top: 5px solid rgba(255,255,255,0.5);
    border-left: 5px solid rgba(255,255,255,0.5);
    border-right: 5px solid rgba(0,0,0,0.2);
    border-bottom: 5px solid rgba(0,0,0,0.2);
    color: #555;
  }
</style>

<svelte:window on:keydown={handleKeydown}/>

<div style="text-align: center">
  {#if key}
    <kbd>{key === ' ' ? 'Space' : key}</kbd>
    <p>{keyCode}</p>
    {:else}
      <p>Focus this window and press any key</p>
    {/if}
</div>

<svelte:window> binding


We can also bind to certain properties of window, such as scrollY.

<svelte:window bind:scrollY={y}/>

The list of properties you can bind to is as follows:

  • innerWidth
  • innerHeight
  • outerWidth
  • outerHeight
  • scrollX
  • scrollY
  • online — an alias for window.navigator.onLine
All except scrollX and scrollY are readonly. You can see interactive example here.

<svelte:body>


Similar to  <svelte:window>, the <svelte:body> element allows you to listen for events that fire on document.body. This is useful with the mouseenter and mouseleave events, which don't fire on window.

For example you can add the mouseenter and mouseleave handlers to the <svelte:body> tag:

<svelte:body
  on:mouseenter={handleMouseenter}
  on:mouseleave={handleMouseleave}
/>

You can see interactive example here.

<svelte:head>


The <svelte:head> element allows you to insert elements inside the <head> of your document:

<svelte:head>
  <link rel="stylesheet" href="tutorial/dark-theme.css">
</svelte:head>

<h1>Hello world!</h1>

In server-side rendering (SSR) mode, contents of <svelte:head> are returned separately from the rest of your HTML.

<svelte:options>


Lastly, <svelte:options> allows you to specify compiler options.

We'll use the immutable option as an example. In this app, the <Todo> component flashes whenever it receives new data. Clicking on one of the items toggles its done state by creating an updated todos array. This causes the other <Todo> items to flash, even though they don't end up making any changes to the DOM.

We can optimise this by telling the <Todo> component to expect immutable data. This means that we're promising never to mutate the todo prop, but will instead create new todo objects whenever things change. This is content of Todo.svelte:

<svelte:options immutable={true}/>

<script>
  import { afterUpdate } from 'svelte';
  import flash from './flash.js';

  export let todo;
  let div;

  afterUpdate( () => { flash(div); } );
</script>

<style>
  div {
    cursor: pointer;
    line-height: 1.5;
  }
</style>

<!-- the text will flash red whenever
     the `todo` object changes -->
<div bind:this={div} on:click>
  {todo.done ? '👍': ''} {todo.text}
</div>

This is flash.js:

export default function flash(element) {
  requestAnimationFrame(() => {
    element.style.transition = 'none';
    element.style.color = 'rgba(255,62,0,1)';
    element.style.backgroundColor = 'rgba(255,62,0,0.2)';

    setTimeout(() => {
      element.style.transition = 'color 1s, background 1s';
      element.style.color = '';
      element.style.backgroundColor = '';
    });
  });
}

And, this is App.svelte:

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

  let todos = [
    { id: 1, done: true, text: 'wash the car' },
    { id: 2, done: false, text: 'take the dog for a walk' },
    { id: 3, done: false, text: 'mow the lawn' }
  ];

  function toggle(toggled) {
    todos = todos.map(todo => {
      if (todo === toggled) {
        // return a new object
        return {
          id: todo.id,
          text: todo.text, 
          done: !todo.done
        };
      }

      // return the same object
      return todo;
    });
  }
</script>

<h2>Todos</h2>
{#each todos as todo}
  <Todo {todo} on:click={() => toggle(todo)}/>
{/each}

Now, when you toggle todos by clicking on them, only the updated component flashes.

The options that can be set here are:

  • immutable = {true} — you never use mutable data, so the compiler can do simple referential equality checks to determine if values have changed
  • immutable = {false} — the default. Svelte will be more conservative about whether or not mutable objects have changed
  • accessors = {true} — adds getters and setters for the component's props
  • accessors = {false} — the default
  • namespace = "..." — the namespace where this component will be used, most commonly "svg"
  • ag = "..." — the name to use when compiling this component as a custom element
Consult the API reference for more information on these options.




Indonesia


<svelte:self>


Svelte menyediakan bermacam-macam elemen built-in. Yang pertama,  <svelte:self>, memungkinkan sebuah komponen untuk membentuk diri sendiri di dalam dirinya secara rekursif.

Komponen ini berguna untuk aplikasi tampilan folder dalam bentuk tree, di mana dalam folder bisa terdapat folder lagi. Pada Folder.svelte kita ingin melakukan seperti ini...

{#if file.type === 'folder'}
  <Folder {...file}/>
{:else}
  <File {...file}/>
{/if}

...tapi itu tidak mungkin, karena sebuah file tidak bisa mengimpor dirinya sendiri. Maka kita akan menggunakan <svelte:self>:

{#if file.type === 'folder'}
  <svelte:self {...file}/>
{:else}
  <File {...file}/>
{/if}

Contoh lengkap bisa dilihat di sini.

<svelte:component>


Sebuah komponen bisa mengubah kategorinya sekaligus dengan <svelte:component>. Daripada menggunakan sekumpulan blok if...

{#if selected.color === 'red'} <RedThing/>
{:else if selected.color === 'green'} <GreenThing/>
{:else if selected.color === 'blue'} <BlueThing/>
{/if}

...kita bisa menggunakan sebuah komponen yang dinamis:

<svelte:component this={selected.component}/>

Isi dari this bisa berupa constructor komponen apapun, atau suatu nilai salah — Jika bernilai salah, maka tidak ada komponen yang dibentuk. Berikut adalah contoh lengkap:

<script>
  import RedThing from './RedThing.svelte';
  import GreenThing from './GreenThing.svelte';
  import BlueThing from './BlueThing.svelte';

  const options = [
    { color: 'red',   component: RedThing   },
    { color: 'green', component: GreenThing },
    { color: 'blue',  component: BlueThing  },
  ];

  let selected = options[0];
</script>

<select bind:value={selected}>
  {#each options as option}
    <option value={option}>{option.color}</option>
  {/each}
</select>

<svelte:component this={selected.component}/>

Ini adalah contoh isi dari RedThing.svelte:

<style>
  strong { color: red; }
</style>

<strong>red thing</strong>

<svelte:window>


Sama halnya dengan kita bisa menambahkan listener even pada elemen DOM apapun, kita juga bisa menambahkan listener even pada object window dengan <svelte:window>.

<script>
  let key;
  let keyCode;

  function handleKeydown(event) {
    key = event.key;
    keyCode = event.keyCode;
  }
</script>

<style>
  div {
    display: flex;
    height: 100%;
    align-items: center;
    justify-content: center;
    flex-direction: column;
  }

  kbd {
    background-color: #eee;
    border-radius: 4px;
    font-size: 6em;
    padding: 0.2em 0.5em;
    border-top: 5px solid rgba(255,255,255,0.5);
    border-left: 5px solid rgba(255,255,255,0.5);
    border-right: 5px solid rgba(0,0,0,0.2);
    border-bottom: 5px solid rgba(0,0,0,0.2);
    color: #555;
  }
</style>

<svelte:window on:keydown={handleKeydown}/>

<div style="text-align: center">
  {#if key}
    <kbd>{key === ' ' ? 'Space' : key}</kbd>
    <p>{keyCode}</p>
    {:else}
      <p>Focus this window and press any key</p>
    {/if}
</div>

<svelte:window> binding


Kita juga bisa menambahkan bind pada properti tertentu dari window, contohnya scrollY.

<svelte:window bind:scrollY={y}/>

Daftar properti yang bisa di bind adalah sebagai berikut:
  • innerWidth
  • innerHeight
  • outerWidth
  • outerHeight
  • scrollX
  • scrollY
  • online — sebuah alias untuk window.navigator.onLine
Semuanya kecuali scrollX dan scrollY adalah readonly. Contoh lengkap bisa dilihat di sini.

<svelte:body>


Mirip dengan <svelte:window>, elemen <svelte:body> memungkinkan kita untuk bisa menangkap even yang berjalan di document.body. Hal ini berguna untuk even mouseenter dan mouseleave, yang tidak berjalan di window.

Sebagai contoh kita bisa menambahkan handler mouseenter dan mouseleave ke tag <svelte:body>:

<svelte:body
  on:mouseenter={handleMouseenter}
  on:mouseleave={handleMouseleave}
/>

Contoh lengkap bisa dilihat di sini.

<svelte:head>


Elemen <svelte:head> memungkinkan kita untuk memasukan elemen di dalam <head> dari dokumen kita:

<svelte:head>
  <link rel="stylesheet" href="tutorial/dark-theme.css">
</svelte:head>

<h1>Hello world!</h1>

Pada mode server-side rendering (SSR), isi dari <svelte:head> akan di return secara terpisah dari keseluruhan HTML kita.

<svelte:options>


Terakhir, <svelte:options> memungkinkan kita untuk mengatur option dari compiler.

Kita akan menggunakan option immutable sebagai contoh. Pada app berikut, komponen <Todo> akan berkedip kapanpun ada data baru yang masuk. Mengklik pada salah satu item akan mengubah status done nya dengan membuat array todos yang sudah diupdate. Hal ini menyebabkan item <Todo> yang lain juga berkedip meskipun tidak terjadi perubahan apapun pada DOMnya.

Kita bisa memperbaiki ini dengan menginformasikan pada komponen <Todo> untuk menganggap data immutable. Ini berarti kita berjanji untuk tidak mengubah properti todo, tapi akan membuat object todo baru jika ada perubahan yang terjadi. Ini adalah isi dari Todo.svelte:

<svelte:options immutable={true}/>

<script>
  import { afterUpdate } from 'svelte';
  import flash from './flash.js';

  export let todo;
  let div;

  afterUpdate( () => { flash(div); } );
</script>

<style>
  div {
    cursor: pointer;
    line-height: 1.5;
  }
</style>

<!-- the text will flash red whenever
     the `todo` object changes -->
<div bind:this={div} on:click>
  {todo.done ? '👍': ''} {todo.text}
</div>

Ini adalah flash.js:

export default function flash(element) {
  requestAnimationFrame(() => {
    element.style.transition = 'none';
    element.style.color = 'rgba(255,62,0,1)';
    element.style.backgroundColor = 'rgba(255,62,0,0.2)';

    setTimeout(() => {
      element.style.transition = 'color 1s, background 1s';
      element.style.color = '';
      element.style.backgroundColor = '';
    });
  });
}

Dan ini adalah App.svelte:

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

  let todos = [
    { id: 1, done: true, text: 'wash the car' },
    { id: 2, done: false, text: 'take the dog for a walk' },
    { id: 3, done: false, text: 'mow the lawn' }
  ];

  function toggle(toggled) {
    todos = todos.map(todo => {
      if (todo === toggled) {
        // return a new object
        return {
          id: todo.id,
          text: todo.text, 
          done: !todo.done
        };
      }

      // return the same object
      return todo;
    });
  }
</script>

<h2>Todos</h2>
{#each todos as todo}
  <Todo {todo} on:click={() => toggle(todo)}/>
{/each}

Sekarang, ketika kita men toggle todos dengan mengklik, hanya komponen terupdate saja yang akan berkedip.

Option lain yang bisa diatur adalah:

  • immutable = {true} — kita tidak pernah menggunakan data yang bisa mutate, jadi compiler akan melakukan pengecekan persamaan referensi secara sederhana untuk menentukan apakah ada nilai yang berubah
  • immutable = {false} — nilai default. Svelte akan lebih konservatif dalam menentukan object terjadi perubahan atau tidak
  • accessors = {true} — menambahkan getter dan setter pada properti komponen
  • accessors = {false} — nilai default
  • namespace = "..." — namespace di mana komponen ini akan digunakan, paling umum adalah "svg"
  • ag = "..." — nama yang akan digunakan ketika meng compile komponen sebagai elemen kustom
Lihat pada API reference untuk informasi lebih lanjut pada option.