List Rendering — Vue

v-for const buah = ref(["Apel", "Mangga", "Jeruk"]) ---template--- {{ item }} Dengan Index const items = ref(["A", "B", "C"]) ---template--- {{ index + 1 }}…

v-for mengulang satu elemen untuk setiap item di sebuah array. Bayangin Bu Sri bikin kartu menu. Dia gak menulis tiap kartu dengan tangan. Dia punya satu cetakan kartu, lalu mencapnya sekali untuk setiap baris di buku catatan menunya. Tambah satu baris di buku, muncul satu kartu baru. Di Vue, cetakan itu elemen yang diberi v-for, buku catatannya array. Supaya Bu Sri tahu kartu mana yang harus dicopot saat satu menu habis, tiap kartu diberi label nama: itu :key.

🧪 Tebakan cepat: Tiap baris menu punya input catatan, dan :key-nya nomor urut baris. Kamu mengetik "pedas" di baris Nasi Goreng, lalu Nasi Goreng dihapus karena habis. Sekarang "pedas" ada di baris mana? Tebak dulu, jawabannya ada di bagian "Kenapa :key penting".

v-for: satu elemen per item

const menu = ref([
  { id: 1, nama: "Nasi Goreng", harga: 15000 },
  { id: 2, nama: "Mie Ayam", harga: 12000 },
  { id: 3, nama: "Es Teh", harga: 5000 },
])

---template---
<ul>
  <li v-for="m in menu" :key="m.id">{{ m.nama }}: Rp{{ m.harga }}</li>
</ul>

Hasil render-nya:

<ul>
  <li>Nasi Goreng: Rp15000</li>
  <li>Mie Ayam: Rp12000</li>
  <li>Es Teh: Rp5000</li>
</ul>

Nomor urut: (item, index)

Dengan menu yang sama di script:

---template---
<ul>
  <li v-for="(m, i) in menu" :key="m.id">{{ i + 1 }}. {{ m.nama }}</li>
</ul>
i Yang tampil
0 1. Nasi Goreng
1 2. Mie Ayam
2 3. Es Teh

v-for atas object dan angka

const pesanan = reactive({ menu: "Nasi Goreng", jumlah: 2, catatan: "pedas" })

---template---
<ul>
  <li v-for="(nilai, kunci, i) in pesanan" :key="kunci">{{ i }}. {{ kunci }}: {{ nilai }}</li>
</ul>
<button v-for="n in 3" :key="n">Meja {{ n }}</button>
Yang ditulis Hasilnya
(nilai, kunci, i) in pesanan 0. menu: Nasi Goreng, 1. jumlah: 2, 2. catatan: pedas
n in 3 tombol Meja 1, Meja 2, Meja 3

Kenapa :key penting

const menu = ref([
  { id: 1, nama: "Nasi Goreng" },
  { id: 2, nama: "Mie Ayam" },
  { id: 3, nama: "Es Teh" },
])

function hapus(id) {
  menu.value = menu.value.filter((m) => m.id !== id)
}

---template---
<ul>
  <li v-for="(m, i) in menu" :key="i">
    {{ m.nama }} <input placeholder="catatan" />
    <button @click="hapus(m.id)">Habis</button>
  </li>
</ul>
Langkah Dengan :key="i" Dengan :key="m.id"
Ketik "pedas" di baris Nasi Goreng Nasi Goreng [pedas], Mie Ayam [ ], Es Teh [ ] sama
Klik Habis di baris Nasi Goreng Mie Ayam [pedas], Es Teh [ ] Mie Ayam [ ], Es Teh [ ]

Dengan :key="i", "pedas" pindah ke Mie Ayam. Itu jawaban tebakannya. Sesudah Nasi Goreng dihapus, Mie Ayam ada di posisi 0, jadi key-nya 0, sama dengan key baris Nasi Goreng tadi. Vue menyimpulkan baris ber-key 0 masih ada, memakai ulang <li> itu, dan cuma mengganti teksnya. Ketikan di input disimpan di elemennya, bukan di data, jadi ikut tertinggal. Yang dibuang malah <li> terakhir, ber-key 2.

Dengan :key="m.id", Vue melihat key 1 hilang dan membuang <li> Nasi Goreng beserta input-nya.

📌 v-for dan v-if di elemen yang sama

---template---
<ul>
  <li v-for="m in menu" v-if="m.stok > 0" :key="m.id">{{ m.nama }}</li>
</ul>

Di Vue 3, v-if dijalankan sebelum v-for, saat m belum ada. Render-nya berhenti dengan TypeError: Cannot read properties of undefined (reading 'stok'), dan seluruh komponen gak tampil. Build development juga menulis [Vue warn]: Property "m" was accessed during render but is not defined on instance. Saring dulu dengan computed:

const tersedia = computed(() => menu.value.filter((m) => m.stok > 0))
// template: <li v-for="m in tersedia" :key="m.id">{{ m.nama }}</li>

Atau pindahkan v-for ke <template v-for="m in menu" :key="m.id"> dan taruh <li v-if="m.stok > 0"> di dalamnya.

⚠️ Jebakan yang sering ketemu

TL;DR: <li v-for="m in menu" :key="m.id">{{ m.nama }}</li> membuat satu <li> per item; directive-nya di elemen yang diulang. (m, i) in menu memberi index mulai 0, (nilai, kunci, i) in object mengulang properti, dan n in 3 memberi 1, 2, 3. :key adalah label nama tiap baris: harus unik dan tetap, biasanya id dari data. Dengan index sebagai key, menghapus satu baris membuat isian input pindah ke baris berikutnya, karena Vue memakai ulang elemen menurut key-nya. v-for tanpa :key adalah kesalahan, dan v-if di elemen yang sama dijalankan duluan, jadi saring dulu dengan computed.

Yang akan kamu pelajari