Directive adalah atribut khusus berawalan v- yang menyuruh Vue melakukan sesuatu pada sebuah elemen. v-if, v-for, dan v-model itu directive bawaan. Custom directive adalah directive buatanmu sendiri, misalnya v-focus atau v-rupiah, yang menyentuh elemen DOM-nya langsung di momen tertentu. Bayangin stempel harga di Warung Bu Sri. Kertas apa pun yang dicap, nota, label, atau papan menu, langsung tercetak "Rp15.000" dengan rapi. Stempelnya gak peduli kertasnya apa, dia bekerja langsung di kertas itu. Tapi kalau harganya naik, kertasnya harus dicap ulang. Custom directive itu stempel untuk elemen, dan Vue yang menekannya di momen yang kamu pilih.
🧪 Tebakan cepat:
v-rupiahcuma punya hookmounted, yang menulis"Rp" + totalke elemennya. Awalnyatotal15000, lalu tombol Tambah Nasi Goreng diklik dantotaljadi 30000.<p v-rupiah="total">sekarang menulis apa? Tebak dulu, jawabannya ada di bagian "Hook: kapan directive dipanggil".
Directive pertama: v-focus
Kotak "Cari menu" di Warung Bu Sri sebaiknya langsung siap diketik begitu halamannya tampil:
<!-- CariMenu.vue -->
<script setup>
const vFocus = {
mounted(el) {
el.focus()
},
}
</script>
<template>
<input v-focus placeholder="Cari menu" />
</template>
- Directive adalah objek berisi hook, yaitu fungsi yang dipanggil Vue di momen tertentu.
mounteddipanggil setelah elemennya terpasang di halaman. eladalah elemen DOM aslinya, di sini<input>.el.focus()menaruh kursor di sana, jadi pembeli bisa langsung mengetik "mie".- Di
<script setup>, variabel yang namanya diawalivlalu huruf besar otomatis bisa dipakai sebagai directive:vFocusjadiv-focus,vRupiahjadiv-rupiah. Tanpa awalan itu, Vue gak mengenalinya. - Ini kode yang sama dengan
onMounteddan template ref dari pelajaran lifecycle:onMounted(() => cari.value.focus()). Bedanya, directive membungkus kode itu supaya elemen mana pun cukup diberi satu atribut.
Hook: kapan directive dipanggil
| Hook | Kapan dipanggil |
|---|---|
created |
elemennya dibuat, atribut dan listener belum dipasang |
beforeMount |
tepat sebelum elemennya masuk ke halaman |
mounted |
elemennya sudah ada di halaman |
beforeUpdate |
komponennya mau render ulang |
updated |
komponennya selesai render ulang |
beforeUnmount |
tepat sebelum elemennya dilepas |
unmounted |
elemennya sudah dilepas, misalnya karena v-if jadi false |
Sekarang v-rupiah yang cuma punya mounted:
const vRupiah = {
mounted(el, binding) {
el.textContent = "Rp" + binding.value.toLocaleString("id-ID")
},
}
<p v-rupiah="total"></p>
<button @click="total += 15000">Tambah Nasi Goreng</button>
| Kejadian | total |
Hook yang jalan | Isi <p>, cuma mounted |
Isi <p>, mounted + updated |
|---|---|---|---|---|
| awal | 15000 | mounted |
Rp15.000 | Rp15.000 |
| Tambah Nasi Goreng | 30000 | beforeUpdate, updated |
Rp15.000 | Rp30.000 |
| Tambah Nasi Goreng | 45000 | beforeUpdate, updated |
Rp15.000 | Rp45.000 |
mounted cuma jalan sekali, waktu elemennya pertama dipasang. Jadi setelah diklik, tulisannya tetap Rp15.000 walau total sudah 30000. Itu jawaban tebakannya. Supaya ikut berubah, tambahkan updated dengan isi yang sama:
const vRupiah = {
mounted(el, binding) {
el.textContent = "Rp" + binding.value.toLocaleString("id-ID")
},
updated(el, binding) {
el.textContent = "Rp" + binding.value.toLocaleString("id-ID")
},
}
Karena isi mounted dan updated sering sama, Vue punya bentuk singkat: directive yang ditulis sebagai fungsi dipanggil di mounted dan di updated.
const vRupiah = (el, binding) => {
el.textContent = "Rp" + binding.value.toLocaleString("id-ID")
}
binding: nilai, argumen, modifier
Argumen kedua setiap hook adalah binding, isi dari atribut directive-nya. Untuk <p v-rupiah:singkat.tebal="total">:
| Bagian | Isinya |
|---|---|
binding.value |
hasil ekspresi total, misalnya 30000 |
binding.oldValue |
nilai sebelumnya, cuma ada di beforeUpdate dan updated, misalnya 15000 |
binding.arg |
teks sesudah titik dua: "singkat" |
binding.modifiers |
objek dari setiap titik: { tebal: true } |
- Isi tanda kutip adalah JavaScript, sama seperti
:harga.v-warna="biru"membaca variabelbiru. Untuk teks, tulis kutip di dalam kutip:v-warna="'blue'". argdanmodifiersmembuat satu directive punya beberapa gaya:v-rupiah:singkatbisa menulisRp30 rb.
Mendaftarkan directive
<script setup>: cukup variabel berawalanv, sepertivFocus. Berlaku di komponen itu saja.- Komponen tanpa
<script setup>: lewat opsidirectives: { focus: vFocus }. Namanya ditulis tanpav-, dipakai sebagaiv-focus. - Global:
app.directive("focus", vFocus)dimain.js, sebelumapp.mount("#app"). Setelah ituv-focusbisa dipakai di semua komponen.
Directive yang dipasang di tag komponen, misalnya <MenuCard v-rupiah="15000" />, mendarat di root element komponen itu, seperti atribut di pelajaran attribute inheritance. Kalau komponennya punya dua root element, directive-nya diabaikan. Build development menulis [Vue warn]: Runtime directive used on component with non-element root node. The directives will not function as intended.
Di editor latihan
Editor latihan meng-compile template di browser, dan trik nama vFocus cuma dikenali oleh build tool yang meng-compile file .vue. Di editor, const vFocus plus <input v-focus> gak melakukan apa-apa. Build development menulis [Vue warn]: Failed to resolve directive: focus; Playground memakai build production, jadi diam saja. Caranya: tulis komponen sebagai objek dengan opsi directives, lalu pasang dengan <component :is>.
const vFocus = { mounted(el) { el.focus() } }
const vRupiah = (el, binding) => {
el.textContent = "Rp" + binding.value.toLocaleString("id-ID")
}
const Warung = {
directives: { focus: vFocus, rupiah: vRupiah },
setup() {
const total = ref(15000)
return { total }
},
template: `
<div>
<input v-focus placeholder="Cari menu" />
<p>Total: <span v-rupiah="total"></span></p>
<button @click="total += 15000">Tambah Nasi Goreng</button>
</div>
`,
}
---template---
<component :is="Warung" />
- Latihan pertama memakai bentuk ini untuk
v-focus:vFocusdiisimounted(el) { el.focus() }, childPencarianmendaftarkannya lewatdirectives: { focus: vFocus }, dan<input id="auto-focus" v-focus />di template-nya. Pemeriksanya memastikan fokus browser benar-benar ada di#auto-focus. - Latihan kedua adalah
v-rupiahdi childKasir: hookmounteddanupdatedmengisiel.textContentdengan"Rp" + binding.value.toLocaleString("id-ID"), jadi#totalberbunyiRp15.000, laluRp30.000sesudah tombol#tambahdiklik.
Kapan pakai directive
Pakai custom directive cuma kalau kamu memang perlu menyentuh elemen DOM-nya langsung: memberi fokus, memasang listener di document lalu melepasnya di unmounted, atau memantau kapan gambar menu terlihat di layar. Kalau yang dibutuhkan cuma teks olahan dari data, fungsi biasa seperti {{ rupiah(total) }} atau computed lebih jelas. v-rupiah di pelajaran ini contoh untuk melihat hook dan binding bekerja.
⚠️ Jebakan yang sering ketemu
- Nama tanpa awalan v.
const focus = { ... }di<script setup>gak tersambung kev-focus. Build development menulis[Vue warn]: Failed to resolve directive: focus. Tulisconst vFocus. - Cuma
mounteduntuk nilai yang berubah. Tulisannya basi setelah data berubah. Tambahkanupdated, atau tulis directive-nya sebagai fungsi. - Memberi fokus di setup. Saat setup jalan, elemennya belum ada dan
inputRef.valuemasihnull, jadi JavaScript menolak denganCannot read properties of null (reading 'focus'). Panggilfocus()dionMountedatau di hookmounted. updatedketinggalan di latihan kedua. Denganmountedsaja,#totaltetapRp15.000sesudah#tambahdiklik. Tulisupdateddengan isi yang sama, atau tulisvRupiahsebagai fungsi.
TL;DR: Custom directive adalah objek hook yang menyentuh elemen DOM langsung: mounted(el, binding) jalan setelah elemen terpasang, updated setelah komponen render ulang, unmounted setelah elemen dilepas. Hook yang cuma mounted gak mengikuti perubahan data; tambahkan updated atau tulis directive sebagai fungsi. binding.value adalah nilai ekspresinya, binding.arg teks sesudah titik dua, binding.modifiers objek dari setiap titik. Di <script setup> nama variabelnya wajib berawalan v (vFocus jadi v-focus); di objek komponen lewat opsi directives; secara global lewat app.directive. Di editor latihan, pakai opsi directives di komponen objek dan pasang komponennya dengan <component :is>. Untuk sekadar mengolah teks, pakai computed.