Custom Directives — Vue

Membuat Directive const vFocus = { mounted(el) { el.focus() } } ---template--- Directive dengan Parameter const vColor = { mounted(el, binding) {…

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-rupiah cuma punya hook mounted, yang menulis "Rp" + total ke elemennya. Awalnya total 15000, lalu tombol Tambah Nasi Goreng diklik dan total jadi 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>

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 }

Mendaftarkan directive

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" />

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

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.

Yang akan kamu pelajari