Proyek Svelte — Svelte

Saatnya membangun proyek mini menggunakan konsep yang sudah dipelajari! Spesifikasi: Todo App Buat aplikasi Todo sederhana dengan fitur: Tambah todo baru…

Proyek Svelte adalah satu aplikasi yang memakai banyak pelajaran sekaligus: route dan layout dari SvelteKit, data dari load, store untuk keranjang, form action untuk pesanan, dan komponen dengan {#each}, bind:, serta $:. Bayangin Warung Bu Sri di hari pertama buka. Dapur, etalase, kasir, dan papan nama sudah pernah kamu siapkan satu per satu; sekarang semuanya dipasang di satu bangunan dan dipakai bersama. Bangunan itu folder proyeknya, ruangannya route, papan namanya layout, dan buku catatan yang dibaca semua ruangan adalah store.

🧪 Tebakan cepat: di daftar belanja, fungsi tambah() ditulis belanja.push(barang) alih-alih belanja = [...belanja, barang]. Kamu mengetik "Beras" lalu menekan Tambah. Beras muncul di daftar atau gak? Jawabannya ada di bagian "Kenapa belanja = [...belanja, barang]".

Peta proyek Warung Bu Sri

Semua file ini sudah kamu tulis di pelajaran sebelumnya. Di satu proyek, mereka bertemu:

File Isinya Dari pelajaran
src/lib/menu.js data menu, pura-pura database Load Data
src/lib/keranjang.js custom store: tambah, hapus, kosongkan Custom Stores
src/routes/+layout.svelte header, nav, jumlah keranjang, <slot /> Routing, Stores
src/routes/menu/+page.js dan +page.svelte load menu, tombol Tambah ke keranjang Load Data, Event
src/routes/pesan/+page.server.js dan +page.svelte form pesanan dengan fail dan redirect Form Actions
src/routes/belanja/+page.svelte daftar belanja Bu Sri ke pasar pelajaran ini

Layout-nya membaca store, jadi jumlah keranjang tampil di semua halaman:

<!-- src/routes/+layout.svelte -->
<script>
  import { keranjang } from "#lib/keranjang.js"
</script>

<header>
  <h1>Warung Bu Sri</h1>
  <nav>
    <a href="/menu">Menu</a>
    <a href="/belanja">Belanja</a>
  </nav>
  <p>Keranjang: {$keranjang.length} menu</p>
</header>

<slot />
<!-- src/routes/menu/+page.svelte -->
<script>
  import { keranjang } from "#lib/keranjang.js"
  export let data
</script>

<h2>Menu</h2>
<ul>
  {#each data.menu as m (m.id)}
    <li>{m.nama} <button on:click={() => keranjang.tambah(m.nama)}>Tambah</button></li>
  {/each}
</ul>

Satu halaman utuh: daftar belanja

Halaman baru proyek ini adalah daftar belanja Bu Sri ke pasar. Dia memakai hampir semua yang ada di jalur Svelte dalam satu komponen:

<!-- src/routes/belanja/+page.svelte -->
<script>
  let belanja = []
  let input = ""
  let nextId = 1

  $: sisa = belanja.filter((b) => !b.beres).length

  function tambah() {
    if (!input.trim()) return
    belanja = [...belanja, { id: nextId++, nama: input, beres: false }]
    input = ""
  }

  function hapus(id) {
    belanja = belanja.filter((b) => b.id !== id)
  }
</script>

<h2>Belanja ke Pasar</h2>
<p>{sisa} belum dibeli dari {belanja.length}</p>

<form on:submit|preventDefault={tambah}>
  <input bind:value={input} placeholder="Mau beli apa?" />
  <button>Tambah</button>
</form>

<ul>
  {#each belanja as b (b.id)}
    <li>
      <input type="checkbox" bind:checked={b.beres} />
      <span class:beres={b.beres}>{b.nama}</span>
      <button on:click={() => hapus(b.id)}>Hapus</button>
    </li>
  {/each}
</ul>

<style>
  .beres { text-decoration: line-through; opacity: 0.5; }
</style>

Dicoba dengan compiler Svelte 4.2.19:

Langkah belanja Layar
awal [] 0 belum dibeli dari 0, daftar kosong
ketik "Beras", Tambah 1 barang 1 belum dibeli dari 1, kolomnya kosong lagi
ketik "Telur", Tambah 2 barang 2 belum dibeli dari 2
centang Beras Beras beres: true 1 belum dibeli dari 2, Beras dicoret
Tambah dengan kolom kosong tetap 2 gak ada baris baru
Hapus Telur 1 barang 0 belum dibeli dari 1

Kenapa belanja = [...belanja, barang]

Di Svelte 4, halaman diperbarui saat ada assignment ke variabel, yaitu tanda =. belanja.push(barang) mengubah array di memori, tapi gak ada assignment, jadi Svelte gak tahu ada yang berubah: daftar dan sisa tetap 0 belum dibeli dari 0. Jadi Beras gak muncul. Itu jawaban tebakannya. Polanya selalu spread lalu assignment ulang: belanja = [...belanja, barang] untuk menambah, belanja = belanja.filter(...) untuk menghapus. Svelte 5 dengan $state berbeda, dan dibahas di pelajaran Runes.

Yang hidup di halaman, dan yang hidup di store

let belanja hidup di dalam komponen halaman. Pindah dari /belanja ke /menu, SvelteKit melepas komponen itu, dan isinya ikut hilang; kembali ke /belanja, daftarnya kosong lagi. Keranjang gak hilang, karena store keranjang hidup di modul src/lib/keranjang.js, bukan di halaman, dan layout tetap terpasang. Aturannya: data yang cuma dipakai satu halaman cukup let di halaman itu; data yang dibaca halaman lain atau layout dipindah ke store.

Latihan: Todo App dan Shopping List

Kedua latihan adalah daftar seperti di atas, sebagai aplikasi berdiri sendiri, jadi judulnya <h1>.

Di keduanya, tombolnya bertuliskan persis Tambah, kolomnya kosong lagi setelah item masuk, dan Tambah dengan kolom kosong gak membuat <li>.

⚠️ Jebakan yang sering ketemu

TL;DR: Proyek Svelte menyatukan semua pelajaran: route dan +layout.svelte dengan <slot />, load untuk data, store di src/lib untuk data yang dipakai banyak halaman (keranjang di header), form action untuk pesanan, dan komponen halaman. Satu halaman daftar memakai let belanja = [], bind:value={input}, <form on:submit|preventDefault={tambah}>, {#each belanja as b (b.id)}, bind:checked, class:, dan $: sisa = belanja.filter((b) => !b.beres).length. Di Svelte 4, layar cuma diperbarui lewat assignment, jadi menambah ditulis belanja = [...belanja, barang] (spread lalu assignment ulang) dan menghapus belanja = belanja.filter(...); push gak memperbarui layar. Tolak kolom kosong dengan if (!input.trim()) return dan kosongkan dengan input = "". let di halaman hilang saat pindah halaman; store tetap. Latihannya: Todo App dengan kata Belajar Svelte dan Shopping List dengan Kopi, tombolnya Tambah.

Yang akan kamu pelajari