Showing posts with label svelte. Show all posts
Showing posts with label svelte. Show all posts

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.

2020-04-27

Svelte Tutorial 15: Context API (English & Indonesia)

English

Context API


The context API provides a mechanism for components to 'talk' to each other without passing around data and functions as props, or dispatching lots of events. It's an advanced feature, but a useful one.

Take this example app using a Mapbox GL map. We'd like to display the markers, using the <MapMarker> component, but we don't want to have to pass around a reference to the underlying Mapbox instance as a prop on each component. Full example can be seen here.

setContext and getContext


There are two halves to the context API — setContext and getContext. If a component calls setContext(key, context), then any child component can retrieve the context with const context = getContext(key).

Let's set the context first. In Map.svelte, import setContext from svelte and key from mapbox.js and call setContext:

<script> // this is Map.svelte
  import { onMount, setContext } from 'svelte';
  import { mapbox, key } from './mapbox.js';

  // set the context here...
  setContext(key,{ getMap: () => map });

  export let lat;
  export let lon;
  export let zoom;

  let container;
  let map;

  onMount(() => {
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'https://unpkg.com/mapbox-gl/dist/mapbox-gl.css';

    link.onload = () => {
      map = new mapbox.Map({
        container,
        style: 'mapbox://styles/mapbox/streets-v9',
        center: [lon, lat],
        zoom
      });
    };

    document.head.appendChild(link);

    return () => {
      map.remove();
      link.parentNode.removeChild(link);
    };
  });
</script>

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

<div bind:this={container}>
  {#if map} <slot></slot> {/if}
</div>

The context object can be anything you like. Like lifecycle functionsetContext and getContext must be called during component initialisation; since map isn't created until the component has mounted, our context object contains a getMap function rather than map itself.

On the other side of the equation, in MapMarker.svelte, we can now get a reference to the Mapbox instance:

<script>
  import { getContext } from 'svelte';
  import { mapbox, key } from './mapbox.js';
  
  // ...get the context here
  const { getMap } = getContext(key);
  const map = getMap();

  export let lat;
  export let lon;
  export let label;

  const popup = new mapbox.Popup({ offset: 25 })
    .setText(label);

  const marker = new mapbox.Marker()
    .setLngLat([lon, lat])
    .setPopup(popup)
    .addTo(map);
</script>

The markers can now add themselves to the map. Here is example of App.svelte, that use Map and MapMarker component.

<script>  import Map from './Map.svelte';  import MapMarker from './MapMarker.svelte';</script>

<Map lat={35} lon={-84} zoom={3.5}>
  <MapMarker lat={37.8225} lon={-122.0024} 
    label="Svelte Body Shaping"/>
  <MapMarker lat={33.8981} lon={-118.4169} 
    label="Svelte Barbershop & Essentials"/>
  <MapMarker lat={29.7230} lon={-95.4189} 
    label="Svelte Waxing Studio"/>
  <MapMarker lat={28.3378} lon={-81.3966} 
    label="Svelte 30 Nutritional Consultants"/>
  <MapMarker lat={40.6483} lon={-74.0237} 
    label="Svelte Brands LLC"/>
  <MapMarker lat={40.6986} lon={-74.4100} 
    label="Svelte Medical Systems"/>
</Map>

Context keys


In mapbox.js you'll see this line:

const key = {};

We can use anything as a key — we could do setContext('mapbox',...)for example. The downside of using a string is that different component libraries might accidentally use the same one; using an object literal means the keys are guaranteed not to conflict in any circumstance (since an object only has referential equality to itself, i.e. {} !== {} whereas "x" ==="x"), even when you have multiple different contexts operating across many component layers. This is full content of mapbox.js:

import mapbox from 'mapbox-gl';
// https://docs.mapbox.com/help/glossary/access-token/
mapbox.accessToken = MAPBOX_ACCESS_TOKEN;
const key = {};
export { mapbox, key };



Contexts vs. stores


Contexts and stores seem similar. They differ in that stores are available to any part of an app, while a context is only available to a component and its descendants. This can be helpful if you want to use several instances of a component without the state of one interfering with the state of the others.

In fact, you might use the two together. Since context is not reactive, values that change over time should be represented as stores:

const { these, are, stores } = getContext(...);



Indonesia


Context API


Context API memberikan mekanisme bagi komponen untuk 'bicara' satu sama lain tanpa perlu mem pass kan data dan fungsi sebagai properti, atau melakukan banyak dispatch even. Ini adalah fitur tingkat tinggi, yang sangat berguna.

Sebagai contoh ini adalah app yang menggunakan peta Mapbox GL. Kita ingin menampilkan marker (=penanda) pada peta, menggunakan komponen <MapMarker>, tapi kita tidak ingin harus repot-repot mem pass kan referensi ke Mapbox sebagai properti pada masing-masing komponen. Contoh lengkap bisa dilihat di sini.

setContext and getContext


Ada 2 bagian dari context API — setContext dan getContext. Jika suatu komponen memanggil setContext(key, context), maka setiap komponen anaknya akan bisa mengakses context dengan const context = getContext(key).

Pertama kita perlu mengeset context dulu di Map.svelte, impor setContext dari svelte dan key dari mapbox.js dan panggil setContext:

<script> // this is Map.svelte
  import { onMount, setContext } from 'svelte';
  import { mapbox, key } from './mapbox.js';

  // set the context here...
  setContext(key,{ getMap: () => map });

  export let lat;
  export let lon;
  export let zoom;

  let container;
  let map;

  onMount(() => {
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'https://unpkg.com/mapbox-gl/dist/mapbox-gl.css';

    link.onload = () => {
      map = new mapbox.Map({
        container,
        style: 'mapbox://styles/mapbox/streets-v9',
        center: [lon, lat],
        zoom
      });
    };

    document.head.appendChild(link);

    return () => {
      map.remove();
      link.parentNode.removeChild(link);
    };
  });
</script>

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

<div bind:this={container}>
  {#if map} <slot></slot> {/if}
</div>

Object dari context bisa berupa apapun yang kita suka. Seperti fungsi lifecyclesetContext dan getContext harus dipanggil saat inisialisasi komponen; karena map tidak akan dibentuk sampai komponen sudah di mount, maka context object kita akan berisi fungsi getMap dan bukan map.

Sementara di sisi lain, pada MapMarker.svelte, kita sekarang bisa mendapatkan referensi ke  Mapbox:

<script>
  import { getContext } from 'svelte';
  import { mapbox, key } from './mapbox.js';
  
  // ...get the context here
  const { getMap } = getContext(key);
  const map = getMap();

  export let lat;
  export let lon;
  export let label;

  const popup = new mapbox.Popup({ offset: 25 })
    .setText(label);

  const marker = new mapbox.Marker()
    .setLngLat([lon, lat])
    .setPopup(popup)
    .addTo(map);
</script>


Marker sekarang sudah bisa ditambahkan ke peta. Berikut adalah contoh dari App.svelte, yang menggunakan komponen Map dan MapMarker.

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

  import MapMarker from './MapMarker.svelte';

</script>

<Map lat={35} lon={-84} zoom={3.5}>
  <MapMarker lat={37.8225} lon={-122.0024} 
    label="Svelte Body Shaping"/>
  <MapMarker lat={33.8981} lon={-118.4169} 
    label="Svelte Barbershop & Essentials"/>
  <MapMarker lat={29.7230} lon={-95.4189} 
    label="Svelte Waxing Studio"/>
  <MapMarker lat={28.3378} lon={-81.3966} 
    label="Svelte 30 Nutritional Consultants"/>
  <MapMarker lat={40.6483} lon={-74.0237} 
    label="Svelte Brands LLC"/>
  <MapMarker lat={40.6986} lon={-74.4100} 
    label="Svelte Medical Systems"/>
</Map>


Context keys


Pada mapbox.js kita akan melihat baris ini:

const key = {};


Kita bisa menggunakan apapun sebagai kunci — contohnya kita bisa menggunakan setContext('mapbox',...). Tapi kekurangan dari menggunakan string sebagai kunci adalah library komponen yang berbeda bisa saja secara kebetulan menggunakan string yang sama; menggunakan object literal berarti kunci yang digunakan tidak akan mengalami konflik pada situasi apapun (karena sebuah object hanya akan dianggap sama dengan dirinya sendiri, contoh {} !== {} sementara "x" ==="x"), bahkan ketika kita mempunyai beberapa context yang berbeda pada banyak tingkatan komponen. Ini adalah isi lengkat dari mapbox.js:

import mapbox from 'mapbox-gl';
// https://docs.mapbox.com/help/glossary/access-token/
mapbox.accessToken = MAPBOX_ACCESS_TOKEN;
const key = {};
export { mapbox, key };


Contexts vs. stores


Context dan store terlihat mirip. Perbedaannya adalah store bisa digunakan pada semua bagian dari app, sementara context hanya tersedia untuk komponen itu dan turunannya. Hal ini bisa bermanfaat jika kita ingin menggunakan beberapa komponen tanpa ada state nya yang saling campur aduk dengan state dari komponen lain.

Faktanya, kita bisa menggunakan context dan store bersamaan. Tapi satu hal yang harus diingat karena context tidak reactive, nilai yang akan berubah-ubah sebaiknya disimpan pada store:

const { these, are, stores } = getContext(...);

2020-04-25

Svelte Tutorial 14: Component Composition (English & Indonesia)

English

Slots


Just like elements can have children...

<div>
  <p>I'm a child of the div</p>
</div>

...so can components. Before a component can accept children, though, it needs to know where to put them. We do this with the <slot> element. Put this inside Box.svelte:

<style>
  .box {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
    margin: 0 0 1em 0;
  }
</style>

<div class="box">
  <slot></slot>
</div>

You can now put things in the Box component in main app:

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

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything!</p>
</Box>

Slot Fallbacks


A component can specify fallbacks for any slots that are left empty, by putting content inside the <slot> element:

<div class="box">
  <slot>
    <em>no content was provided</em>
  </slot>
</div>

We can now create instances of <Box> without any children, you can try this from previous example:

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything.</p>
</Box>

<Box/>

Named Slots


The previous example contained a default slot, which renders the direct children of a component. Sometimes you will need more control over placement, such as with this <ContactCard>. In those cases, we can use named slots.

In ContactCard.svelte, add a name attribute to each slot:

<style>
  .contact-card {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
  }

  h2 {
    padding: 0 0 0.2em 0;
    margin: 0 0 1em 0;
    border-bottom: 1px solid #ff3e00
  }

  .address, .email {
    padding: 0 0 0 1.5em;
    background:  0 0 no-repeat;
    background-size: 20px 20px;
    margin: 0 0 0.5em 0;
    line-height: 1.2;
  }

  .address { background-image: url(tutorial/icons/map-marker.svg) }
  .email   { background-image: url(tutorial/icons/email.svg) }
  .missing { color: #999 }
</style>

<article class="contact-card">
  <h2>
    <slot name="name">
      <span class="missing">Unknown name</span>
    </slot>
  </h2>

  <div class="address">
    <slot name="address">
      <span class="missing">Unknown address</span>
    </slot>
  </div>

  <div class="email">
    <slot name="email">
      <span class="missing">Unknown email</span>
    </slot>
  </div>
</article>

Then, add elements with corresponding slot="..." attributes inside the <ContactCard> component:

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

<ContactCard>
  <span slot="name"> P. Sherman </span>
  <span slot="address"> 42 Wallaby Way, Sydney </span>
</ContactCard>

Slot Props


In this app, we have a <Hoverable> component that tracks whether the mouse is currently over it. It needs to pass that data back to the parent component, so that we can update the slotted contents.

For this, we use slot props. In Hoverable.svelte, pass the hovering value into the slot by adding hovering={hovering} in slot.

<script> // this is Hoverable.svelte
  let hovering;
  function enter() { hovering = true; }
  function leave() { hovering = false; }
</script>

<div on:mouseenter={enter} on:mouseleave={leave}>
  <slot hovering={hovering}></slot>
</div>

Then, to expose hovering to the contents of the<Hoverable> component, we use the let directive:

<Hoverable let:hovering={hovering}>
  <div class:active={hovering}>
    {#if hovering} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

You can rename the variable, if you want — let's call it active in the parent component. You can also have as many of these components as you like, and the slotted props will remain local to the component where they're declared.

<script> // this is App.svelte
  import Hoverable from './Hoverable.svelte';
</script>

<style>
  div {
    padding: 1em;
    margin: 0 0 1em 0;
    background-color: #eee;
  }

  .active {
    background-color: #ff3e00;
    color: white;
  }
</style>

<Hoverable let:hovering={active}>
  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

Named slots can also have props; use the let directive on an element with a slot="..." attribute, instead of on the component itself.




Indonesia

Slots


Sama halnya seperti elemen bisa mempunyai child...

<div>
  <p>I'm a child of the div</p>
</div>

...begitu juga komponen juga bisa. Sebelum komponen bisa menerima child, komponen perlu diberi tahu dulu di mana posisinya akan diletakkan. Kita bisa melakukan ini dengan elemen <slot>. Berikut contoh Box.svelte:

<style>
  .box {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
    margin: 0 0 1em 0;
  }
</style>

<div class="box">
  <slot></slot>
</div>

Sekarang kita bisa meletakkan sesuatu pada komponen Box pada main app:

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

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything!</p>
</Box>

Slot Fallbacks


Sebuah komponen bisa menentukan fallback untuk slot apapun yang dibiarkan kosong, dengan meletakkan isian pada elemen <slot>:

<div class="box">
  <slot>
    <em>no content was provided</em>
  </slot>
</div>

Sekarang kita bisa membuat komponen <Box> tanpa diisi child, disini kita akan coba dari contoh sebelumnya:

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything.</p>
</Box>

<Box/>

Named Slots


Contoh sebelumnya kita melihat slot default, yang akan me-render child langsung dari komponen. Tapi terkadang kita akan membutuhkan pengaturan lebih dalam peletakan, contoh pada <ContactCard> berikut. Pada kasus seperti ini kita bisa gunakan named slot (=slot yang dinamai).

Pada ContactCard.svelte, tambahkan atribut name pada setiap slot:

<style>
  .contact-card {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
  }

  h2 {
    padding: 0 0 0.2em 0;
    margin: 0 0 1em 0;
    border-bottom: 1px solid #ff3e00
  }

  .address, .email {
    padding: 0 0 0 1.5em;
    background:  0 0 no-repeat;
    background-size: 20px 20px;
    margin: 0 0 0.5em 0;
    line-height: 1.2;
  }

  .address { background-image: url(tutorial/icons/map-marker.svg) }
  .email   { background-image: url(tutorial/icons/email.svg) }
  .missing { color: #999 }
</style>

<article class="contact-card">
  <h2>
    <slot name="name">
      <span class="missing">Unknown name</span>
    </slot>
  </h2>

  <div class="address">
    <slot name="address">
      <span class="missing">Unknown address</span>
    </slot>
  </div>

  <div class="email">
    <slot name="email">
      <span class="missing">Unknown email</span>
    </slot>
  </div>
</article>

Kemudian tambahkan elemen dengan atribut slot="..." yang sesuai di dalam komponen <ContactCard>:

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

<ContactCard>
  <span slot="name"> P. Sherman </span>
  <span slot="address"> 42 Wallaby Way, Sydney </span>
</ContactCard>

Slot Props


Pada app berikut, kita mempunyai komponen <Hoverable> yang akan mendeteksi jika pointer mouse berada di atasnya. komponen ini perlu mem pass data ke komponen parent nya, supaya kita bisa meng update isi dari slot.

Di sini kita bisa menggunakan slot props. Di dalam Hoverable.svelte, pass nilai hovering ke dalam slot dengan menambahkan hovering={hovering} pada <slot>.

<script> // this is Hoverable.svelte
  let hovering;
  function enter() { hovering = true; }
  function leave() { hovering = false; }
</script>

<div on:mouseenter={enter} on:mouseleave={leave}>
  <slot hovering={hovering}></slot>
</div>

Kemudian, untuk mengekspos hovering pada isi dari komponen <Hoverable>, kita bisa menggunakan direktiv let:

<Hoverable let:hovering={hovering}>
  <div class:active={hovering}>
    {#if hovering} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

Kita juga bisa mengganti nama variabel jika mau — misal diganti jadi active pada komponen parent. Kita juga bisa mempunyai lebih dari satu komponen jika mau, dan slot prop akan tetap bersifat lokal pada komponen tempat dideklarasikan.

<script> // this is App.svelte
  import Hoverable from './Hoverable.svelte';
</script>

<style>
  div {
    padding: 1em;
    margin: 0 0 1em 0;
    background-color: #eee;
  }

  .active {
    background-color: #ff3e00;
    color: white;
  }
</style>

<Hoverable let:hovering={active}>
  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>

  <div class:active>
    {#if active} <p>I am being hovered upon.</p>
    {:else} <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

Named slot juga bisa memiliki properti, gunakan direktiv let pada elemen dengan atribut slot="...", dan bukan pada komponennya.