Vue JS Reactive Data: Konsep Dasar dan Directives - Eps 3

Saatnya Kita Bahas Hal Seru: Reactive Data dan Directives

Yo, welcome back! Kalo kalian udah ikutin episode sebelomnya di sini: Cara Install Vue JS dan Setup Project dengan Create-Vue - Eps 2, berarti projectnya udah jalan dan siap buat kita mainkan. Mantep deh!

Nah, sekarang kita masuk ke bagian yang beneran bikin Vue.js jadi keren. Jangan cuma setup doang dong, kita mau langsung paham gimana sih inernya Vue.js bekerja. Gampangnya sih, di episode ini kita bakal belajar gimana caranya bikin data yang otomatis berubah dan update tampilannya tanpa kita suruh-suruh. Keren kan?

Kali ini fokusnya ada tiga hal penting banget: reactive data yang bakal bikin data kita hidup, directives yang bikin kita bisa kontrol UI dengan mudah, dan computed properties yang kaya punya asisten pribadi yang hitung-hitungan buat kita. Habis paham ketiga hal itu, langsung kita bikin Counter App yang istemewa sebagai mini project pertama kita.

Jadi siap gak? Mari kita mulai perjalanan seru ini dan lihat kenapa banyak developer remote di BuildWithAngga yang sukses pakai Vue.js!

Konsep Reactivity: Bikin Data yang Hidup dan Responsif

Oke jadi gini, bayangkan aja kalau kita pakai JavaScript biasa. Kita ada variable count yang nilainya 0, terus kita tambah jadi 1. Lalu apa? UI-nya tetep menampilkan 0. Pas banget, kan? Data udah berubah tapi tampilan di layar sama sekali gak berubah. Ini yang bikin frustrasi banyak developer awal.

Nah, disinilah Vue.js masuk dan ngasih solusi yang elegant. Dengan Vue.js, kita cukup buat data pakai cara khusus, terus Vue bakal automatis nge-track perubahan data kita. Pas data berubah, tampilan langsung update tanpa kita perlu lakukan apa-apa. Mantap kan?

Mari kita liat perbedaannya. Di JavaScript tradisional kita nulis begini:

let count = 0
count++ // UI tidak update, data doang yang berubah

Sedangkan di Vue.js, kita nulis:

const count = ref(0)
count.value++ // UI otomatis update, magic banget!

Jadi Vue.js punya sistem yang bisa "nonton" data kita. Kalau ada perubahan, langsung dia update tampilannya. Itulah yang namanya reactivity, dan inilah yang bikin Vue.js jadi begitu powerful untuk frontend development.

Ref untuk Nilai Primitif: String, Number, Boolean

Ref untuk Nilai Primitif: String, Number, Boolean
Ref untuk Nilai Primitif: String, Number, Boolean

Kalo kita mau pake reactivity di Vue.js, kita butuh import dulu ref dari library Vue. Ref itu singkatannya dari reference, yang berarti kita bikin referensi reaktif untuk nilai kita.

Untuk nilai primitif kayak string, angka, atau boolean, kita pakai ref(). Caranya gampang aja:

import { ref } from 'vue'

const message = ref('Hello')    // String
const count = ref(0)            // Number
const isActive = ref(true)      // Boolean

Nah, sekarang kita udah punya tiga variable reaktif. Setiap kali kita ubah nilai-nilai ini, Vue otomatis bakal update tampilannya. Tapi ada trik khusus nih kalau kita mau akses atau update nilainya.

Di JavaScript, kita perlu pakai .value buat akses atau ubah nilainya:

// Akses nilai
console.log(count.value)  // Output: 0

// Update nilai
count.value++             // Sekarang count.value = 1

Tapi tunggu, di template Vue, kita gak perlu pakai .value. Vue udah cukup smart untuk handle itu sendiri:

<p>{{ count }}</p>  <!-- Langsung tampil, tanpa .value -->

Jadi kasarnya, di file JavaScript .value itu wajib, tapi di template udah optional. Vue bakal otomatis unwrap value-nya buat kita. Praktis banget kan?

Reactive untuk Object dan Array

Selain ref(), Vue juga punya reactive() yang digunakan khusus buat object dan array. Cara pakainya juga cukup simple:

import { reactive } from 'vue'

const user = reactive({
  name: 'Budi',
  age: 25
})

Pas kita pakai reactive(), kita bisa langsung akses dan ubah property-nya tanpa perlu .value:

user.name = 'Siti'  // Langsung update, gak perlu .value
user.age++          // Kini user.age = 26

Di template juga sama, langsung bisa pakai tanpa .value:

<p>{{ user.name }}</p>  <!-- Tampil "Siti" -->
<p>{{ user.age }}</p>   <!-- Tampil "26" -->

Kelihatannya reactive() lebih simple dibanding ref() karena gak perlu .value, tapi ada perlakuan khusus kalau kita mau ganti object selamanya. Dengan reactive() kita gak bisa gitu, sedangkan dengan ref() bisa.

Kapan Pakai Ref dan Kapan Pakai Reactive

Untuk pemula seperti kalian, rekomendasi kami sih pakai ref() aja dulu. Kenapa? Karena ref() lebih konsisten, fleksibel, dan bisa handle semua kasus.

Untuk nilai primitif kayak string, angka, atau boolean, pasti pakai ref(). Kalo mau pakai reactive() untuk object, itu juga boleh, tapi ref() tetap lebih rekomended buat pemula karena lebih straightforward dan gak ada surprise behavior.

Jadi intinya, kalau kita mau bikin reactive data yang betah-betah dan gak pusing, gunakan ref() buat segalanya. Itu cara yang paling aman dan paling mudah dipahami.

Template Syntax: Bahasa yang Vue Mengerti di HTML Kita

Sekarang kita udah tahu cara bikin reactive data, langkah selanjutnya adalah gimana caranya nge-display data itu di template Vue kita. Nah, Vue punya syntax khusus buat ini semua, dan ini yang namanya template syntax. Jangan takut ya, sebenarnya gampang banget dan akan bikin hidup kita jadi jauh lebih mudah.

Bayangin aja kalau kita punya data di JavaScript, terus kita pengen masukin ke HTML. Vue punya beberapa cara untuk lakuin itu, dan setiap cara punya use case sendiri-sendiri. Mulai dari yang paling simple sampai yang agak kompleks, kita bahas semua.

Text Interpolation: Menampilkan Data Langsung di Template

Yang paling basic dan sering kita pakai adalah text interpolation. Ini adalah cara paling simple buat display data di HTML. Caranya kita pakai dua kurung kurawal {{ }} yang biasanya disebut mustache syntax.

Misal kita ada data message yang isinya "Hello BuildWithAngga", terus kita pengen tampilin di halaman:

<p>{{ message }}</p>  <!-- Tampil: Hello BuildWithAngga -->

Tapi text interpolation di Vue gak cuma bisa nampilkan data aja, lho. Kita bisa juga nge-compute langsung di dalamnya. Misal kita punya count dengan nilai 10, terus kita pengen tampilin hasilnya ditambah 1:

<p>{{ count + 1 }}</p>  <!-- Tampil: 11 -->

Atau bahkan kita bisa pake ternary operator buat conditional:

<p>{{ count > 5 ? 'Banyak' : 'Sedikit' }}</p>  <!-- Tampil: Banyak -->

Tapi jangan terlalu banyakan logic di sini ya. Kalau kita perlu logic yang kompleks, lebih baik pindahin ke computed property atau method. Template itu harusnya clean dan readable, bukan tempat buat nulis logic yang rumit.

Attribute Binding: Kasih Tahu Vue Kalau Ini Data Dinamis

Attribute Binding: Kasih Tahu Vue Kalau Ini Data Dinamis
Attribute Binding: Kasih Tahu Vue Kalau Ini Data Dinamis

Sekarang gimana kalau kita pengen bind data ke attribute HTML? Misal kita punya variable imageUrl dan kita pengen masukin ke src dari tag img. Kalau kita nulis begini:

<img src="{{ imageUrl }}" />  <!-- Ini SALAH! Gak bakal jalan -->

Nggak bakal jalan karena Vue gak mengerti kalau src attribute itu dynamic. Kita perlu kasih tahu Vue pake directive v-bind:

<!-- Long form, gak recommended -->
<img v-bind:src="imageUrl" />

<!-- Shorthand, ini yang recommended -->
<img :src="imageUrl" />

Kita prefer shorthand dengan : karena lebih ringkas dan readable. Jadi mulai sekarang, kalo ada attribute yang mau kita bind dengan data, pakai : aja.

Ada banyak attribute yang bisa kita bind. Misal untuk link:

<a :href="url">Buka BuildWithAngga</a>

Atau untuk button yang mau kita disable berdasarkan kondisi:

<button :disabled="isDisabled">Klik di Sini</button>

Dynamic Class dan Style: Styling yang Responsif terhadap Data

Salah satu kekuatan template binding adalah kita bisa ubah class dan style berdasarkan data. Ini super berguna buat UI yang interactive.

Untuk dynamic class, kita pakai syntax object dimana key adalah nama class dan value adalah kondisi boolean:

<div :class="{ active: isActive }">
  <!-- Class 'active' akan ditambah kalau isActive true -->
</div>

Kalau isActive adalah true, maka div ini akan punya class active. Kalau false, class tidak akan ada.

Sama juga dengan style, kita bisa bikin dynamic dengan pakai object:

<div :style="{ color: textColor }">
  <!-- Color akan berubah sesuai value dari textColor -->
</div>

Kalo textColor variablenya 'red', maka text color div akan jadi merah. Kalau 'blue', jadi biru. Simple banget kan?

Event Handling: Buat Vue Respond terhadap User Action

Event Handling: Buat Vue Respond terhadap User Action
Event Handling: Buat Vue Respond terhadap User Action

Yang paling exciting adalah kita bisa make Vue respond terhadap aksi user kayak klik, input, dan sebagainya. Caranya pakai directive v-on atau shorthand @.

Bayangkan kita ada function handleClick dan kita pengen trigger function itu pas button diklik:

<!-- Long form, tidak recommended -->
<button v-on:click="handleClick">Klik di Sini</button>

<!-- Shorthand, ini yang recommended -->
<button @click="handleClick">Klik di Sini</button>

Shorthand @ jauh lebih elegant dan itu yang standard di komunitas Vue sekarang. Jadi selalu pakai yang @ aja.

Event handling bukan cuma click aja. Kita bisa handle banyak event lainnya. Misalnya kita punya input field dan kita pengen trigger function pas user tekan Enter key:

<input @keyup.enter="handleEnter" />

Di sini kita pake .enter modifier buat specify bahwa function handleEnter hanya di-trigger pas Enter key di-press. Vue punya banyak modifier seperti ini yang bikin ngoding jadi lebih efisien dan kurang error.

Jadi untuk ringkas-ringkasnya, pakai : untuk attribute binding dan @ untuk event handling. Dua shorthand ini yang paling penting buat di-inget karena akan kalian pakai setiap hari dalam ngoding Vue.js di BuildWithAngga!

Directives: Perintah Khusus Vue yang Bikin UI Jadi Dinamis

Sekarang kita masuk ke bagian yang benar-benar powerful dan bikin Vue.js jadi berbeda dari framework lain. Directive adalah instruksi khusus Vue yang kita tambahkan ke HTML element untuk kasih tahu Vue apa yang harus dikerjakan. Bayangkan directive sebagai perintah-perintah magic yang bisa ngatur tampilan dan behavior dari elemen HTML kita.

Kita bakal belajar empat directive yang paling penting dan paling sering dipakai: v-if untuk conditional rendering, v-show untuk show-hide element, v-for untuk render list, dan v-model untuk dua arah data binding. Mari kita belajar masing-masing dengan detil.

Conditional Rendering dengan V-If dan V-Else

Conditional Rendering dengan V-If dan V-Else
Conditional Rendering dengan V-If dan V-Else

Baik, jadi kita punya scenario dimana kita hanya pengen nampilkan element tertentu kalau kondisi terpenuhi. Misal kita punya aplikasi yang ada fitur login, dan kita hanya pengen nampilkan pesan "Welcome!" kalau user udah login.

Caranya kita pakai directive v-if:

<p v-if="isLoggedIn">Welcome!</p>
<p v-else>Please login</p>

Kalau variable isLoggedIn adalah true, maka elemen pertama bakal ditampilkan. Kalau false, yang ditampilkan adalah elemen kedua. Mudah kan?

Tapi kalau kita punya lebih dari dua kondisi, kita bisa pakai v-else-if. Misal kita punya sistem nilai di aplikasi BuildWithAngga:

<div v-if="score >= 90">A</div>
<div v-else-if="score >= 75">B</div>
<div v-else>C</div>

Vue bakal check kondisi satu per satu. Kalau score 95, tampilnya "A". Kalau score 80, tampilnya "B". Kalau kurang dari 75, tampilnya "C". Logikanya sama seperti JavaScript if-else, tapi di template.

Penting yang perlu di-inget adalah v-if itu menghapus element dari DOM selamanya kalau kondisi tidak terpenuhi. Jadi kalau isLoggedIn adalah false, elemen tersebut benar-benar gak ada di DOM sama sekali, bukan cuma di-hide.

V-Show: Alternatif yang Lebih Efficient untuk Toggle

V-Show: Alternatif yang Lebih Efficient untuk Toggle
V-Show: Alternatif yang Lebih Efficient untuk Toggle

Sekarang pertanyaannya, apa kalau kita punya element yang sering di-toggle antara visible dan hidden? Misal ada sidebar yang bisa dibuka dan ditutup berkali-kali, terus kita pakai v-if, Vue bakal terus menambah dan menghapus element dari DOM berulang kali. Ini bisa jadi inefficient.

Untuk kasus seperti itu, kita bisa pakai v-show:

<p v-show="isVisible">Toggle me</p>

Perbedaannya dengan v-if adalah v-show tidak menghapus element dari DOM. Sebaliknya, v-show hanya mengubah CSS display property menjadi none untuk menyembunyikan element. Jadi element tetap ada di DOM, cuma gak terlihat di layar.

Kapan kita pakai v-if dan kapan kita pakai v-show? Intinya gini aja: kalau element jarang berubah antara visible dan hidden, pakai v-if. Kalau element sering di-toggle, pakai v-show karena lebih efficient dan jauh lebih cepat.

Render List dengan V-For

Render List dengan V-For
Render List dengan V-For

Salah satu fitur paling powerful dari Vue adalah v-for yang bisa render list dengan mudah. Bayangkan kita punya array of tasks di BuildWithAngga:

const tasks = [
  { id: 1, text: 'Belajar Vue.js' },
  { id: 2, text: 'Bikin Counter App' },
  { id: 3, text: 'Bikin Todo List' }
]

Terus kita pengen render semua task dalam bentuk list item. Dengan v-for, kita bisa lakuin ini dengan sangat simple:

<ul>
  <li v-for="item in tasks" :key="item.id">
    {{ item.text }}
  </li>
</ul>

Maka hasilnya adalah tiga li element yang masing-masing menampilkan text dari setiap task. Super mudah!

Kalau kita juga butuh index dari setiap item, kita bisa destructure dan dapat index-nya:

<li v-for="(item, index) in tasks" :key="item.id">
  {{ index }}. {{ item.text }}
</li>

Sekarang kita bakal dapat nomor urut. Item pertama jadi "0. Belajar Vue.js", yang kedua "1. Bikin Counter App", dan seterusnya.

Sekarang ada satu hal yang SANGAT penting yang harus kalian perhatikan: selalu pakai :key dengan unique value! Di contoh kita, kita pakai item.id sebagai key. Ini penting supaya Vue bisa track setiap item dengan benar, terutama kalau list kita di-update, dihapus, atau di-reorder. Kalau kalian lupa pakai key atau pakai index sebagai key, Vue bisa jadi confused dan menyebabkan bug yang sulit di-debug.

Two-Way Data Binding dengan V-Model

Two-Way Data Binding dengan V-Model
Two-Way Data Binding dengan V-Model

Sekarang kita sampai ke directive yang paling nyaman dipakai: v-model. Ini adalah directive yang paling sering dipakai buat form handling.

Bayangkan kita punya input field dan kita pengen bikin kalau user ngetik sesuatu, data kita otomatis terupdate. Dan sebaliknya, kalau data kita berubah dari kode, input field juga otomatis terupdate. Ini yang disebut two-way data binding, dan v-model handle ini dengan sempurna.

Untuk text input:

<input v-model="message" />

Sekarang kalau user ngetik "Hello", maka message variable otomatis jadi "Hello". Dan kalau dari kode kita set message = 'Hi', input field otomatis menunjukin "Hi".

v-model juga bisa dipakai untuk berbagai jenis input. Untuk checkbox:

<input type="checkbox" v-model="checked" />

Kalau checkbox di-check, checked jadi true. Kalau di-uncheck, jadi false.

Untuk radio button:

<input type="radio" value="A" v-model="picked" />
<input type="radio" value="B" v-model="picked" />

Kalau yang di-select adalah radio pertama, picked jadi "A". Kalau yang kedua, picked jadi "B".

Dan untuk select dropdown:

<select v-model="selected">
  <option value="A">Option A</option>
  <option value="B">Option B</option>
</select>

Kalau user select Option A, selected jadi "A".

Ada juga modifier khusus untuk v-model yang bisa customize behavior-nya:

<input v-model.lazy="text" />      <!-- Update hanya saat element kehilangan focus -->
<input v-model.number="age" />     <!-- Auto convert input ke number -->
<input v-model.trim="username" />  <!-- Auto remove whitespace di awal dan akhir -->

.lazy modifier itu berguna kalau kita gak mau update data setiap keystroke, tapi hanya saat user selesai ngetik. .number modifier berguna kalau kita expect number tapi input element kembalikan string. Dan .trim berguna buat remove whitespace yang sering jadi masalah di form handling.

Jadi directive adalah fondasi dari Vue.js reactivity. Dengan menguasai empat directive ini, kalian udah siap bikin aplikasi yang interactive dan responsif terhadap user action!

Computed Properties: Asisten yang Hitung Data Buat Kita

Sekarang kita bakal belajar salah satu fitur paling elegant di Vue.js yang namanya computed properties. Ini adalah cara Vue buat kita punya nilai yang otomatis dihitung dari data lain, dan bakal terupdate setiap kali data dependency-nya berubah. Intinya, ini seperti punya asisten pribadi yang bisa hitung-hitungan buat kita tanpa perlu kita suruh berulang kali.

Jadi kenapa computed properties itu penting? Bayangkan kita punya aplikasi di BuildWithAngga yang punya banyak data yang kompleks. Kalau kita harus hitung ulang setiap kali butuh nilai tersebut, itu akan bikin code kita kotor dan inefficient. Computed properties solve ini dengan elegant.

Apa Itu Computed dan Kenapa Penting

Computed adalah reactive value yang nilainya dihitung dari data lain. Jadi kalau data dependency-nya berubah, computed value juga otomatis berubah. Yang paling keren dari computed adalah dia di-cache berdasarkan dependencies-nya. Artinya kalau dependencies-nya gak berubah, computed value tidak akan dihitung ulang. Ini bikin aplikasi jadi lebih efficient.

Sebagai contoh, kita punya data firstName dan lastName. Kita pengen bikin fullName yang adalah gabungan dari dua data itu. Dengan computed, kita bisa lakuin seperti ini:

import { ref, computed } from 'vue'

const firstName = ref('Budi')
const lastName = ref('Santoso')

const fullName = computed(() => {
  return firstName.value + ' ' + lastName.value
})

Sekarang kalau kita akses fullName, hasilnya adalah "Budi Santoso". Dan kalau kita ubah firstName menjadi "Siti", computed otomatis bakal update fullName menjadi "Siti Santoso" tanpa kita perlu lakukan apa-apa.

Di template, kita bisa langsung pakai computed:

<template>
  <p>{{ fullName }}</p>
</template>

Computed vs Methods: Pilih yang Tepat

Sekarang pertanyaannya, kenapa kita perlu computed kalau kita bisa pakai methods aja? Oke, mari kita banding-bandingkan.

Kalau kita pakai methods, kita bakal nulis:

function getFullName() {
  return firstName.value + ' ' + lastName.value
}

Terus di template:

<p>{{ getFullName() }}</p>  <!-- Perhatian parentheses -->

Sekilas sama aja kan? Tapi ada perbedaan penting di belakang layar.

Computed itu di-cache. Artinya kalau kita akses fullName berkali-kali, tapi dependencies-nya (firstName dan lastName) tidak berubah, Vue tidak perlu hitung ulang. Value yang sudah di-cache langsung dikembalikan. Ini bikin aplikasi lebih cepat, terutama kalau computation-nya kompleks.

Sedangkan methods, setiap kali kita akses (misalnya tiap kali component re-render), function-nya di-call ulang. Kalau computation-nya berat, ini bisa jadi bottleneck.

Jadi rule of thumb-nya gini: pakai computed kalau kita butuh nilai yang dihitung dari data lain dan sering di-akses. Pakai methods kalau kita butuh logic yang lebih kompleks atau kalau ada side effect.

Use Case Praktis: Filter dan Calculate

Use Case Praktis: Filter dan Calculate
Use Case Praktis: Filter dan Calculate

Mari kita liat beberapa contoh praktis dari computed yang sering di-pakai.

Pertama adalah filter list. Misal kita punya aplikasi search di BuildWithAngga. User ngetik keyword di search box, terus kita pengen nampilkan item yang cocok:

const items = ref(['Vue.js', 'React', 'Angular', 'Svelte'])
const search = ref('')

const filteredItems = computed(() => {
  return items.value.filter(item =>
    item.toLowerCase().includes(search.value.toLowerCase())
  )
})

Jadi kalau user ngetik "vue", filteredItems bakal otomatis jadi ['Vue.js']. Kalau user clear search box, filteredItems jadi semua item lagi.

Kedua adalah calculate total. Kalau kita punya e-wallet atau shopping cart di BuildWithAngga:

const cart = ref([
  { id: 1, price: 50000 },
  { id: 2, price: 75000 },
  { id: 3, price: 100000 }
])

const total = computed(() => {
  return cart.value.reduce((sum, item) =>
    sum + item.price, 0
  )
})

Sekarang total bakal otomatis jadi 225000. Dan kalau kita tambah atau hapus item dari cart, total otomatis terupdate. Kita gak perlu hitung ulang manual.

Computed adalah tool yang super powerful untuk bikin code yang clean dan efficient. Jadi gunakan computed kapan pun kita butuh nilai yang dihitung dari data lain!

Methods: Fungsi yang Handle Aksi User dan Logic

Methods: Fungsi yang Handle Aksi User dan Logic
Methods: Fungsi yang Handle Aksi User dan Logic

Nah, sekarang kita pindah ke methods. Kalau computed itu untuk nilai yang dihitung, methods itu untuk action yang kita jalanin. Methods adalah function biasa yang bisa kita panggil dari template atau dari function lain. Ini yang digunakan buat handle event, user action, dan logic yang butuh side effect.

Cara Bikin dan Pakai Methods

Methods itu simple banget caranya. Kita tinggal define function biasa di <script setup>:

import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}

Terus di template, kita bisa trigger function itu dengan @click:

<button @click="increment">+1</button>

Kalau button diklik, function increment bakal dijalanin, dan count bakal bertambah 1. Simple kan?

Methods juga bisa punya parameter. Misal kita punya method yang namanya addNumber yang ngambil parameter num:

function addNumber(num) {
  count.value += num
}

Terus di template, kita bisa pass parameter saat memanggil method:

<button @click="addNumber(5)">+5</button>
<button @click="addNumber(10)">+10</button>

Jadi klik tombol pertama, count bertambah 5. Klik tombol kedua, count bertambah 10. Fleksibel banget!

Kapan Harus Pakai Methods

Methods harus digunakan kalau kita butuh jalanin logic sebagai respond terhadap event atau aksi user. Berikut beberapa use case yang paling umum:

  • Event handlers: Handle klik button, input field, atau event lainnya dari user. Setiap aksi dari user bisa trigger method yang berbeda.
  • User actions kompleks: Kalau user submit form, kita butuh validation, terus kirim data ke server. Semua logic itu ada di method.
  • API calls: Fetch data dari server atau kirim data bisa dilakukan di method. Methods bisa async dengan async-await, jadi perfect buat handle API calls.
  • Data manipulation: Update atau manipulate array, atau update data berdasarkan kondisi tertentu.

Jadi singkatnya, kalau ada logic yang perlu dijalanin sebagai respond terhadap event atau action, itu gunakan methods. Methods adalah cara Vue handle imperative logic yang gak bisa dihandle dengan reactive data dan computed properties.

Mini Project: Counter App Aplikasi Pertama Kita

Mini Project: Counter App Aplikasi Pertama Kita
Mini Project: Counter App Aplikasi Pertama Kita

Nah, sekarang kita udah belajar semua core concepts Vue.js. Saatnya kita langsung praktik dan bikin aplikasi pertama kita. Kita bakal bikin Counter App yang simple tapi menggunakan semua yang udah kita pelajari di sini. Ini adalah aplikasi yang perfect buat memahami bagaimana reactive data, computed, methods, directives, dan event handling bekerja sama.

Counter App itu aplikasi yang punya satu angka di layar. User bisa nambah, kurangi, atau reset angka tersebut. Tapi kita gak cuma bikin yang simple aja, kita juga kasih constraint: angka tidak boleh kurang dari 0 dan tidak boleh lebih dari 100. Plus, kita akan tampilin pesan yang berubah-ubah berdasarkan nilai counter-nya.

Features yang Akan Kita Bikin

Oke jadi aplikasi kita bakal punya beberapa fitur penting:

  • Display counter: Tampilkan angka yang besar dan jelas di tengah layar.
  • Increment: Tombol plus yang ngasih tambah 1 setiap kali diklik, tapi maksimal 100.
  • Decrement: Tombol minus yang ngasih kurang 1 setiap kali diklik, tapi minimal 0.
  • Reset: Tombol buat reset counter kembali ke 0.
  • Disable buttons: Tombol minus di-disable kalau counter sudah 0, dan tombol plus di-disable kalau counter sudah 100.
  • Dynamic message: Tampilkan pesan yang berbeda-beda sesuai nilai counter-nya.

Code Lengkap Counter App

Oke langsung aja kita liat code lengkapnya. Ini adalah implementasi Counter App yang complete dengan semua fitur yang udah kita sebutkan:

<script setup>
import { ref, computed } from 'vue'

// State
const count = ref(0)

// Computed untuk check kondisi button
const canDecrement = computed(() => count.value > 0)
const canIncrement = computed(() => count.value < 100)

// Computed untuk dynamic message
const message = computed(() => {
  if (count.value === 0) return 'Start counting!'
  if (count.value < 50) return 'Keep going...'
  if (count.value === 100) return 'Maximum!'
  return 'Almost there!'
})

// Methods
function increment() {
  if (count.value < 100) count.value++
}

function decrement() {
  if (count.value > 0) count.value--
}

function reset() {
  count.value = 0
}
</script>

<template>
  <div class="app">
    <div class="counter" style="animation: fadeInUp 0.7s ease">
      <h1 class="title">Counter App</h1>

      <div class="display">{{ count }}</div>

      <p class="message">{{ message }}</p>

      <div class="buttons">
        <button class="btn" @click="decrement" :disabled="!canDecrement">-</button>
        <button class="btn reset" @click="reset">Reset</button>
        <button class="btn" @click="increment" :disabled="!canIncrement">+</button>
      </div>
    </div>
  </div>
</template>

<style scoped>
/* Fullscreen layout */
.app {
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;

  background: linear-gradient(135deg, #42b883, #35495e);
  font-family: 'Inter', sans-serif;
}

/* Wrapper */
.counter {
  text-align: center;
  padding: 20px;
  color: white;
  width: 100%;
  max-width: 420px;
}

/* Title */
.title {
  font-size: 3rem;
  font-weight: 700;
  margin-bottom: 20px;
}

/* Count display */
.display {
  font-size: 5rem;
  font-weight: 700;
  margin: 20px 0;
  color: #ffffff;
  text-shadow: 0 4px 15px rgba(0, 0, 0, 0.25);
}

/* Dynamic Message */
.message {
  font-size: 1.4rem;
  margin-bottom: 25px;
  opacity: 0.9;
}

/* Buttons layout */
.buttons {
  display: flex;
  gap: 12px;
  justify-content: center;
}

/* Modern buttons */
.btn {
  background: #42b883;
  color: white;
  padding: 12px 24px;
  font-size: 1.3rem;
  border: none;
  border-radius: 12px;
  cursor: pointer;
  transition: 0.25s ease;
  box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
}

.btn:hover:not(:disabled) {
  background: #37a06e;
  transform: translateY(-4px);
  box-shadow: 0 12px 25px rgba(0, 0, 0, 0.25);
}

.btn:active:not(:disabled) {
  transform: translateY(-1px);
}

/* Reset button (dark theme) */
.reset {
  background: #35495e;
}

.reset:hover {
  background: #2d3e50;
}

/* Disabled state */
.btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* Fade animation */
@keyframes fadeInUp {
  0% {
    opacity: 0;
    transform: translateY(25px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>

Penjelasan Code Step-by-Step

Ini breakdown-nya:

  • Import: Kita ambil ref dan computed dari Vue.
  • State: count adalah reactive data yang di-initialize ke 0.
  • Computed: canDecrement dan canIncrement untuk check kondisi button. message return pesan dinamis sesuai nilai counter.
  • Methods: increment(), decrement(), dan reset() dengan validation range 0-100.
  • Template: Tiga button dengan @click event, dan :disabled bind ke computed untuk disable button sesuai kondisi.
  • Styling: CSS scoped agar hanya berlaku di component ini. Color #42b883 adalah warna Vue.js yang cocok buat BuildWithAngga.

Concepts yang Diaplikasikan

Kalau kita liat code ini, sebenarnya sudah menggunakan semua yang kita pelajari di episode ini:

  • ref() untuk state: Kita pakai ref(0) buat count.
  • computed() untuk derived values: Kita pakai computed buat canDecrement, canIncrement, dan message.
  • Methods untuk actions: increment(), decrement(), dan reset() handle user actions.
  • Event handling dengan @click: Setiap button punya @click yang trigger method.
  • Dynamic binding dengan :disabled: Button di-disable berdasarkan kondisi dari computed.
  • Conditional logic di computed: message return pesan berbeda based on counter value.

Jadi Counter App ini adalah perfect example dari bagaimana semua concepts Vue.js bekerja sama dalam satu aplikasi yang utuh. Coba kalian buat project ini di BuildWithAngga dan eksperimen, misalnya ubah range minimum-maksimum, tambah fitur lain, atau customize styling-nya. Learning by doing adalah cara terbaik buat paham Vue.js!

Kesimpulan: Recap Semua yang Udah Kita Pelajari

Oke, sekarang kita udah selesai episode 3. Ini adalah ringkas-ringkasnya:

Reactive data dengan ref() dan reactive(), template syntax untuk connect JavaScript ke HTML, directives kayak v-if, v-show, v-for, dan v-model, computed properties untuk derived values, methods buat handle actions, dan kita udah praktek membuat Counter App.

Skill yang Udah Kalian Kuasai

  • Manage reactive state
  • Handle user events
  • Conditional rendering
  • Two-way binding dengan v-model
  • Computed values
  • Component structure dengan <script setup>
  • Scoped styling

Apa Selanjutnya

Episode 4 kita bakal bikin Todo List App dengan CRUD functionality. Siap gak? Belajar lebih lanjut dan enroll di kelas Vue JavaScript Framework di BuildWithAngga: https://buildwithangga.com/kelas/vue-javascript-framework

Terima kasih udah ikutin episode ini, sampai jumpa di episode 4!