Ngoding dari Nol

Bab 6

Menyimpan banyak data

Aplikasi uang jajanmu belajar mencatat banyak jajanan dan mengingatnya, bahkan setelah browser ditutup. Plus kenalan sama frontend, backend, dan database.

± 10 menit baca

Di Bab 5, tombol Tambah sudah bisa menghitung. Tapi jujur aja, sebagai catatan uang jajan, aplikasi itu masih payah. Ada dua masalah:

Pertama, dia cuma ingat total. Kamu nggak bisa lihat jajan apa saja yang bikin total-nya segitu. Kedua, begitu halamannya dimuat ulang, semuanya hilang.

Bab ini menyelesaikan keduanya. Dan di ujung bab, aplikasi uang jajanmu selesai.

Daftar belanja: array

Kita butuh tempat menyimpan banyak jajanan, bukan cuma satu angka. Bayangin daftar belanja di kertas: telur, sabun, mie. Nah, di JavaScript, daftar seperti itu disebut ArrayDaftar berisi banyak nilai berurutan, seperti daftar belanja.Penjelasan lengkap →:

let belanja = ['telur', 'sabun', 'mie'];

Isinya diurutkan dan diberi nomor, mulai dari 0 (komputer menghitung dari nol, bukan satu). Jadi telur bernomor 0, sabun nomor 1, mie nomor 2. Untuk menambah barang ke ujung daftar, pakai push:

belanja.push('kopi');

Tapi satu jajanan itu bukan cuma satu kata. Dia punya nama dan harga. Untuk bungkusan seperti ini ada ObjekSatu catatan yang berisi beberapa keterangan berlabel, misalnya nama dan harga.Penjelasan lengkap →, yaitu satu catatan yang punya beberapa keterangan berlabel:

{ nama: 'Bakso', harga: 15000 }

Bayangin satu baris di buku catatan yang punya kolom “nama” dan kolom “harga”. Kalau banyak baris dikumpulkan dalam satu array, jadilah seluruh buku catatan:

let jajanan = [
  { nama: 'Bakso', harga: 15000 },
  { nama: 'Es teh', harga: 5000 },
];

Itulah isi variabel jajanan di aplikasimu. Setiap kali kamu klik Tambah, satu baris baru di-push ke dalamnya:

jajanan.push({ nama: nama, harga: harga });

Menampilkan dan menghapus

Data sudah ada di memori, tapi pengguna butuh melihatnya sebagai daftar di layar. Karena jumlah jajanan nggak pasti (bisa 2, bisa 20), kita nggak mungkin menulis kode per item. Di sinilah PerulanganMenyuruh komputer mengerjakan hal yang sama berulang-ulang tanpa menulis ulang kodenya.Penjelasan lengkap → dipakai, yaitu menyuruh komputer mengerjakan hal yang sama untuk setiap item. Ingat Bab 1, “ulangi terus sampai…”? Ini dia wujudnya:

jajanan.forEach(function (jajan, urutan) {
  const baris = document.createElement('li');
  baris.textContent = jajan.nama + ' — ' + rupiah(jajan.harga);
  // ...(tombol Hapus dibuat dan ditempel di sini)...
  daftar.appendChild(baris);
  total = total + jajan.harga;
});

Bacanya: “Untuk setiap jajan di dalam jajanan (sekalian dengan nomor urutan-nya), buat satu baris daftar baru, isi dengan nama dan harganya, tempelkan ke daftar di layar, lalu tambahkan harganya ke total.” document.createElement('li') membuat elemen baru lewat kode, dan appendChild menempelkannya ke halaman.

Semua kerja “menggambar ulang daftar” dibungkus dalam satu fungsi bernama tampilkan(). Polanya sederhana: setiap kali data berubah, panggil tampilkan(). Dia mengosongkan daftar di layar, lalu menggambar ulang dari data terbaru.

Tombol hapus per jajanan bekerja dengan cara serupa:

jajanan.splice(urutan, 1);
simpan();
tampilkan();

splice(urutan, 1) artinya “mulai dari nomor urutan, buang 1 item”. Setelah itu, tampilkan() menggambar ulang. Sedangkan simpan() (kita isi sebentar lagi) akan mencatat daftarnya.

Satu lagi, di tombol Tambah ada penjaga supaya pengguna nggak menambah jajanan kosong:

if (nama === '' || harga <= 0) {
  alert('Isi dulu nama jajan dan harganya, ya!');
  return;
}

Ini KondisiInstruksi bersyarat, jalankan sesuatu hanya kalau syaratnya terpenuhi.Penjelasan lengkap →: “Kalau nama kosong atau (||) harga nol ke bawah, tampilkan peringatan dan berhenti (return).” Sama seperti “kalau warungnya tutup…” di Bab 1.

Kodenya memang lebih panjang dari Bab 5, tapi isinya cuma gabungan hal yang sudah kamu kenal, ditambah tiga hal baru: array, perulangan, dan kondisi. HTML dan CSS-nya masih yang tadi, ditambah tempat kosong untuk daftar (tag <ul id="daftar">) dan sedikit gaya untuk barisnya.

Masalahnya: datanya hilang

Sekarang bagian yang menyakitkan. Daftar jajanan hidup di memori sementara. Begitu halaman dimuat ulang (karena di-refresh, ditutup, atau kodenya berubah), memori itu dibuang dan jajanan balik jadi [], daftar kosong.

Kamu akan merasakannya sendiri di kotak latihan di bawah. Tambah beberapa jajanan, lalu ubah satu huruf saja di kodenya, dan lihat apa yang terjadi pada daftarmu. Rasanya nyesek. Itu bagus, karena itulah kenapa kita perlu menyimpan.

localStorage: buku catatan milik browser

Untungnya, browser punya tempat penyimpanan kecil untuk setiap situs, namanya localStoragePenyimpanan kecil milik browser yang menyimpan data di perangkatmu dan tetap ada walau halaman ditutup.Penjelasan lengkap →. Bayangin buku catatan milik browser. Kamu bisa menulis sesuatu di dalamnya, menutup halaman, mematikan laptop, lalu besoknya buka lagi, dan catatan itu masih ada.

Cara pakainya cuma dua perintah:

localStorage.setItem('nama', 'Rina');   // tulis: halaman bernama 'nama', isinya 'Rina'
localStorage.getItem('nama');           // baca halaman bernama 'nama' → hasilnya 'Rina'

Kata pertama ('nama') adalah label halaman di buku itu, dan kata kedua adalah isinya.

Tapi ada satu syarat: buku catatan ini hanya bisa menyimpan teks. Padahal jajanan kita bukan teks, melainkan array berisi objek. Solusinya adalah JSONFormat menuliskan data sebagai teks yang rapi, supaya bisa disimpan atau dikirim lalu dibaca lagi.Penjelasan lengkap →, yaitu cara menuliskan data sebagai teks yang rapi. Ada dua perintah pasangan:

JSON.stringify(jajanan)   // data → teks, siap disimpan
JSON.parse(teks)          // teks → data, siap dipakai lagi

Hasil JSON.stringify untuk satu jajanan kira-kira berbentuk [{"nama":"Bakso","harga":15000}]. Itu teks biasa.

Coba sendiri: bikin catatanmu awet

Di bawah ini, aplikasi uang jajan yang sudah bisa menampilkan daftar dan menghapus, tapi belum bisa menyimpan. Dua tugasmu: mengisi fungsi simpan(), lalu membaca data lama waktu halaman dibuka.

Coba Sendiri

<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <title>Uang Jajanku</title>
  <style>
    body {
      font-family: sans-serif;
      background: #fff8e7;
      color: #222;
      margin: 0;
      padding: 16px;
    }

    .kartu {
      max-width: 400px;
      margin: 0 auto;
      background: white;
      padding: 20px;
      border-radius: 16px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    }

    h1 {
      margin-top: 0;
      color: #e8590c;
    }

    label {
      display: block;
      margin-top: 12px;
      font-weight: bold;
    }

    input {
      width: 100%;
      box-sizing: border-box;
      margin-top: 4px;
      padding: 10px;
      border: 2px solid #ddd;
      border-radius: 8px;
      font-size: 16px;
    }

    button {
      width: 100%;
      margin-top: 16px;
      padding: 12px;
      background: #e8590c;
      color: white;
      border: none;
      border-radius: 8px;
      font-size: 16px;
      font-weight: bold;
      cursor: pointer;
    }

    .total {
      margin-top: 20px;
      padding: 12px;
      background: #fff3bf;
      border-radius: 8px;
      text-align: center;
      font-size: 20px;
      font-weight: bold;
    }

    ul {
      padding: 0;
      list-style: none;
    }

    li {
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 8px;
      padding: 8px 0;
      border-bottom: 1px dashed #ddd;
    }

    .hapus {
      width: auto;
      margin: 0;
      padding: 4px 10px;
      background: #eee;
      color: #c00;
      font-size: 14px;
    }
  </style>
</head>
<body>
  <div class="kartu">
    <h1>Uang Jajanku</h1>
    <p>Catat setiap kali kamu jajan.</p>

    <label for="nama">Jajan apa?</label>
    <input id="nama" type="text" placeholder="contoh: Bakso">

    <label for="harga">Harganya berapa?</label>
    <input id="harga" type="number" placeholder="contoh: 15000">

    <button id="tombol-tambah">Tambah</button>

    <ul id="daftar"></ul>

    <p id="total" class="total">Total: Rp 0</p>
  </div>

  <script>
    const inputNama = document.getElementById('nama');
    const inputHarga = document.getElementById('harga');
    const tombol = document.getElementById('tombol-tambah');
    const daftar = document.getElementById('daftar');
    const teksTotal = document.getElementById('total');

    // Daftar jajanan. Untuk sekarang selalu mulai dari daftar kosong.
    let jajanan = [];

    function simpan() {
      // Tantangan: simpan daftar jajanan ke penyimpanan browser
    }

    function rupiah(angka) {
      return 'Rp ' + angka.toLocaleString('id-ID');
    }

    function tampilkan() {
      daftar.innerHTML = '';
      let total = 0;
      jajanan.forEach(function (jajan, urutan) {
        const baris = document.createElement('li');
        baris.textContent = jajan.nama + ' — ' + rupiah(jajan.harga);

        const tombolHapus = document.createElement('button');
        tombolHapus.textContent = 'Hapus';
        tombolHapus.className = 'hapus';
        tombolHapus.addEventListener('click', function () {
          jajanan.splice(urutan, 1);
          simpan();
          tampilkan();
        });

        baris.appendChild(tombolHapus);
        daftar.appendChild(baris);
        total = total + jajan.harga;
      });
      teksTotal.textContent = 'Total: ' + rupiah(total);
    }

    tombol.addEventListener('click', function () {
      const nama = inputNama.value.trim();
      const harga = Number(inputHarga.value);
      if (nama === '' || harga <= 0) {
        alert('Isi dulu nama jajan dan harganya, ya!');
        return;
      }
      jajanan.push({ nama: nama, harga: harga });
      simpan();
      tampilkan();
      inputNama.value = '';
      inputHarga.value = '';
    });

    tampilkan();
  </script>
</body>
</html>

Hasilnya:

Console

    Urutannya:

    1. Coba dulu. Isi nama jajan dan harga (misalnya Bakso, 15000), lalu klik Tambah. Tambah satu lagi. Coba tombol Hapus. Coba juga klik Tambah dengan kotak kosong untuk melihat penjaganya bekerja.
    2. Rasakan masalahnya. Ubah satu huruf di tulisan Catat setiap kali kamu jajan., tunggu sebentar, dan lihat daftarmu lenyap.
    3. Isi simpan(). Di dalamnya, tulis perintah localStorage.setItem dengan label 'jajanan', dan isinya adalah jajanan yang sudah diubah jadi teks lewat JSON.stringify.
    4. Baca data lama. Cari baris let jajanan = []; di atas. Ganti agar isinya diambil dulu dari localStorage (pakai getItem dengan label yang sama, lalu JSON.parse).
    5. Uji. Tambah beberapa jajanan, lalu ubah satu huruf lagi. Kali ini daftarnya harus tetap ada.

    Buntu? Tombol Lihat jawaban menampilkan versi final yang sudah lengkap. Dua baris kuncinya ini:

    let jajanan = JSON.parse(localStorage.getItem('jajanan')) || [];
    
    function simpan() {
      localStorage.setItem('jajanan', JSON.stringify(jajanan));
    }

    Baris pertama dibaca: “Ambil teks berlabel 'jajanan' dari buku catatan, ubah balik jadi data. Atau (||), kalau belum ada, pakai daftar kosong.” Pada pemakaian pertama, buku catatannya memang belum berisi apa-apa, jadi getItem mengembalikan kosong dan || [] menyelamatkan kita. Fungsi simpan() dipanggil setiap kali data berubah (setelah Tambah dan setelah Hapus), jadi buku catatannya selalu terbaru.

    Intip dunia luar: frontend, backend, database

    Sebelum kita rayakan, ada satu konsep besar yang perlu kamu tahu, supaya nggak bingung saat dengar orang-orang ngobrol di dunia ngoding. Cukup konsepnya saja, nggak ada kode.

    Bayangin sebuah restoran:

    Sekarang, lihat aplikasi uang jajanmu. Dia cuma punya ruang makan. Nggak ada dapur dan nggak ada gudang. “Gudang”-nya adalah buku catatan kecil yang dibawa pelanggan sendiri (localStorage), yang tersimpan di HP atau laptop itu.

    Akibatnya:

    Aplikasi yang datanya harus bisa dibuka dari HP mana pun, atau dibagi ke banyak orang, seperti marketplace dan media sosial, butuh dapur dan gudang beneran. Itu belajar lanjutan. Peta jalannya ada di bab terakhir.

    Selamat, aplikasinya sudah jadi! 🎉

    Coba lihat apa yang barusan kamu bikin. Satu aplikasi dengan:

    Semuanya ditulis dalam satu file: HTML sebagai kerangka, CSS sebagai baju, JavaScript sebagai otak, dan localStorage sebagai ingatan. Dan ini bukan sekadar contoh di buku pelajaran. Ini aplikasi sungguhan, dan di bab-bab depan kita bawa keluar dari halaman ini: ke laptopmu, lalu ke internet.

    Tapi tenang, perjalanannya belum selesai. Pasti, suatu saat, kodemu akan error. Itu bukan tanda kamu gagal, itu tanda kamu sedang ngoding beneran. Jadi di Bab 7 kita belajar cara menghadapinya tanpa panik.

    Yang barusan kamu pelajari

    • Array menyimpan banyak nilai dalam urutan (nomornya mulai dari 0). Objek mengelompokkan keterangan berlabel, seperti nama dan harga. push menambah, splice menghapus.
    • Perulangan (forEach) menyuruh komputer mengerjakan hal yang sama untuk setiap item. Pola praktisnya: setiap data berubah, panggil tampilkan() untuk menggambar ulang.
    • Data di variabel hilang saat halaman dimuat ulang. localStorage adalah buku catatan milik browser yang menyimpan data di perangkatmu, hanya dalam bentuk teks, jadi daftar diubah dulu dengan JSON (stringify dan parse).
    • Frontend (ruang makan) adalah yang dilihat pengguna, backend (dapur) mengolah di balik layar, database (gudang) menyimpan data. Aplikasimu baru punya frontend, jadi datanya hanya ada di perangkat itu sendiri.
    • Aplikasi catatan uang jajan sudah jadi: mencatat, menghapus, menjumlah, dan mengingat.