Pendahuluan
Membuat sebuah aplikasi tidak hanya sebatas dapat membuatnya bekerja sesuai yang kita inginkan, melainkan juga memikirkan kemungkinan perubahan dan penambahan fitur dari aplikasi tersebut. Nah, apa jadinya, jika aplikasi kamu sudah cukup kompleks dan beberapa bulan kemudian kamu ingin melakukan perubahan, tapi justru membuatmu kebingungan?
Salah satu fitur lainnya dari Vuex, selain memudahkan kamu dalam manajemen state agar dapat digunakan oleh semua component yang berperan, juga memungkinkan kamu untuk memecah block code yang telah dibuat kedalam Module sesuai dengan peruntukannya masing-masing. Misalnya, kita memiliki 3 buah fitur, yakni: Donatur, Bantuan dan Transaksi. Jika ketiga code untuk meng-handle fitur tersebut disatukan dalam satu buah file, maka mengelolanya akan sangat membingungkan dikemudian hari. Bagaimana tidak? Jika code dari masing-masing fitur saling tercampur baur satu sama lain.
Tahap Persiapan
Meskipun artikel ini merupakan lanjutan dari seri sebelumnya, tapi kita akan memulainya dengan case yang berbeda, yakni: membuat aplikasi donasi. Buat project fresh install terlebih dahulu:
npm create donasi
Pilih Manually select feature, lalu ceklis pilihan berikut :

Masuk ke direktori donasi, install bootstrapVue
cd donasi vue add bootstrap-vue
Menyiapkan Component Helper
Setelah selesai buka editor, kita akan membuat 2 buah component helper, Donatur & Bantuan.
components/Donatur.vue
<template> <div> <div class="form-group"> <label for="">Donatur</label> <select @change="$emit('selectedDonatur', $event.target.value)" class="form-control"> <option value="">Pilih Donatur</option> <option v-for="donatur in listDonatur" :key="donatur.id" :value="donatur.id"> {{ donatur.name }} </option> </select> </div> </div> </template> <script> export default { computed: { //MEMBUAT COMPUTED PROPERTY DENGAN NAMA listDonatur() listDonatur() { //DATA DIAMBIL DARI STATE MODULE donatur return this.$store.state.donatur.listDonatur } } } </script>
components/Bantuan.vue
<template> <div> <div class="form-group"> <label for="">Jenis Bantuan</label> <select @change="$emit('selectedBantuan', $event.target.value)" class="form-control"> <option value="">Pilih Bantuan</option> <option v-for="row in listBantuan" :key="row.id" :value="row.id"> {{ row.name }} </option> </select> </div> </div> </template> <script> export default { computed: { //MEMBUAT COMPUTED PROPERTY DENGAN NAMA listBantuan() listBantuan() { //DATA DIAMBIL DARI STATE MODULE clients return this.$store.state.bantuan.listBantuan } } } </script>
Menyiapkan State Module untuk Component Helper
Selanjutnya kita akan membuat state module untuk kedua component di atas.
modules/donatur.js
const donatur = { namespaced: true, state: { listDonatur: [ { id:'1', name: 'Riski Amelia' }, { id:'2', name: 'Ima Sumadir' }, { id:'3', name: 'Apri Yunita' }, { id:'4', name: 'Tuti Winarti' } ] }, getters: { getNameById: (state) => (id) => { return state.listDonatur.find((obj) => { return obj.id===id }) } }, mutations: { // NONE }, actions: { // NONE } } export default donatur
modules/bantuan.js
const bantuan = { namespaced: true, state: { listBantuan: [ { id:"1", name: 'Gempa Lombok', }, { id:"2", name: 'Beasiswa Pendidikan' }, { id:"3", name: 'Banjir Bandang' }, { id:"4", name: 'Tanggungan Kesehatan' } ] }, getters: { getBantuanById: (state) => (id) => { return state.listBantuan.find((obj) => { return obj.id===id }) } }, mutations: { // NONE }, actions: { // NONE } } export default bantuan
Sebelum ke tahap selanjutnya, saya akan mengajak anda memahami kedua component helper di atas yaitu Donatur.vue dan Bantuan.vue dimana keduanya hanya berisi template select form yang berisi data master dari dua state module yaitu donatur.js dan bantuan.js.
Menyiapkan Component View
Selanjutnya kita akan membuat View Donasi.vue yang berisi component FormDonasi.vue untuk manambahkan transaksi & component Transaksi untuk menampilkan data transaksi.
views/Donasi.vue
<template> <div class="home"> <div class="container" style="padding-top: 20px"> <div class="row"> <div class="col-md-6"> <FormDonasi/> </div> <div class="col-md-6"> <Transaksi/> </div> </div> </div> </div> </template> <script> import FormDonasi from "@/components/FormDonasi.vue"; import Transaksi from "@/components/Transaksi.vue"; export default { name: "home", components: { FormDonasi, Transaksi } }; </script>
component/FormDonasi.vue
<template> <div class="card"> <div class="card-header"> <h3 class="card-title">Form Donasi</h3> <!--<h4 v-if="typeof donatur_row === 'object'">{{donatur_row.name}}</h4> <h4 v-if="typeof bantuan_row === 'object'">{{bantuan_row.name}}</h4>--> </div> <div class="card-body"> <form @submit.prevent="formSubmit"> <list-donatur @selectedDonatur="selectedDonatur" /> <lokasi-bantuan @selectedBantuan="selectedBantuan" /> <div class="form-group"> <label for="">Jumlah Donasi (Rp)</label> <input type="number" v-model="jumlah" class="form-control"> </div> <div class="form-group"> <button class="btn btn-primary btn-sm" :disabled="isLoading"> {{ isLoading ? 'Loading...':'Donasi!' }} </button> </div> </form> </div> </div> </template> <script> import Donatur from "@/components/Donatur.vue"; import Clients from "@/components/Bantuan.vue"; export default { components: { 'list-donatur': Donatur, 'lokasi-bantuan': Clients, }, data() { return { donatur: '', bantuan: '', jumlah: 0 } }, computed: { donatur_row: function () { //MENGAMBIL NAMA DONATUR BERDASARKAN ID MENGGUNAKAN GETTERS return this.$store.getters['donatur/getNameById'](this.donatur); }, bantuan_row: function () { //MENGAMBIL NAMA BANTUAN BERDASARKAN ID MENGGUNAKAN GETTERS return this.$store.getters['bantuan/getBantuanById'](this.bantuan); }, isLoading() { //MENGAMBIL STATE DARI ROOT VUEX (store.js) //JADI, SETELAH .state. LANGSUNG MENYEBUTKAN NAMA STATENYA //KARENA TIDAK MASUK KEDALAM MODULE return this.$store.state.isLoading } }, methods: { //HANDLE EMIT DARI COMPONENT DONATUR selectedDonatur(val) { this.donatur = val; }, //HANDLE EMIT DARI COMPONENT CLIENTS selectedBantuan(val) { this.bantuan = val }, formSubmit(event) { //MENGUBAH ID MENJADI NAMA this.donatur= typeof this.donatur_row === 'object'?this.donatur_row.name:'0'; this.bantuan= typeof this.bantuan_row === 'object'?this.bantuan_row.name:'0'; this.$store.dispatch('transaksi/save_donasi', { //MENGIRIM 4 BUAH PARAMETER YANG AKAN DIPUSH KEDALAM LISTS ARRAY listTransaksi id: Math.random().toString(36).substring(7), donatur: this.donatur, bantuan: this.bantuan, jumlah: this.jumlah }); this.jumlah="0"; event.target.reset(); } } } </script>
Penjelasan mengenai
component/Transaksi.vue
<template> <div class="card"> <div class="card-header"> <h3 class="card-title">Donasi Terkumpul</h3> </div> <div class="card-body"> <div class="table-responsive"> <table class="table table-hover table-bordered"> <thead> <tr> <th>#</th> <th>Donatur</th> <th>Jenis Bantuan</th> <th>Jumlah</th> </tr> </thead> <tbody> <!-- LOOPING LIST ARRAY DARI listTransaksi --> <tr v-for="(row, index) in listTransaksi" :key="index"> <td>{{ row.id }}</td> <td>{{ row.donatur }}</td> <td>{{ row.bantuan }}</td> <td>{{ row.jumlah }}</td> </tr> <tr> <td> </td> <td> </td> <td> </td> <td>{{ total }}</td> </tr> </tbody> </table> </div> </div> </div> </template> <script> import { mapState } from 'vuex' //IMPORT mapState export default { computed: { //MENGGUNAKAN HELPER mapState UNTUK MEMANGGIL MODULE transaksi ...mapState('transaksi', { // DIMANA DATA YANG AKAN DIAMBIL ADALAH STATE listTransaksi listTransaksi: state => state.listTransaksi, }), total() { return this.listTransaksi.map(item => item.jumlah).reduce((total, amount) => total + parseInt(amount)); } } } </script>
Menyiapkan State Module untuk Transaksi
Karena Transaksi.vue merupakan sebuat data maka kita juga akan state module untuk component transaksi yaitu modules/transaksi.js
modules/transaksi.js
const transaksi = { namespaced: true, state: { //DEFAULT DATA TRANSAKSI YANG AKAN DITAMPILKAN //PADA COMPONENT TRANSAKSI.VUE listTransaksi: [ { id: 'TRX1P1', donatur: 'Anugrah Sandi', bantuan: 'Gempa Lombok', jumlah: 100000 }, { id: 'TRX1P2', donatur: 'Dharma', bantuan: 'Banjir Bandang', jumlah: 250000 }, { id: 'TRX1P3', donatur: 'Asis Ramadhan', bantuan: 'Beasiswa Pendidikan', jumlah: 3000000 } ] }, mutations: { //MENGUBAH STATE DENGAN ADD_DONASI: (state, payload) => { //MENAMBAHKAN DATA BARU KEDALAM ARRAY MENGGUNAKAN PUSH() state.listTransaksi.push(payload); } }, actions: { save_donasi({ commit, rootState }, payload) { // rootState BERARTI MENGAKSES STATE YANG TIDAK BERADA DALAM MODULES // DALAM HAL INI STATE isLoading YANG ADA DI DALAM FILE store.js rootState.isLoading = true //SET TRUE UNTUK MEMBERIKAN EFEK LOADING setTimeout(() => { //MENGINSTRUKSIKAN PADA MUTATIONS TERKAIT UNTUK MENJALANKAN INSTRUKSINYA commit('ADD_DONASI', payload) // STATE isLoading DI MATIKAN KEMBALI rootState.isLoading = false }, 1000) } } } export default transaksi
Menambahkan Menu/Link menuju Donasi.vue
Setelah semuanya siap kita akan membuat Link Menu untuk menuju View Donasi dengan cara menambahkan link pada App.vue
src/App.vue
<template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> | <router-link to="/donasi">Donasi</router-link> </div> <router-view /> </div> </template> <style> #app { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } #nav a.router-link-exact-active { color: #42b983; } </style>
Menambahkan Router
Buka file src/router.js lalu tambahkan route berikut :
{ path: "/donasi", name: "Donasi", component: () => import("./views/Donasi.vue") }
Implementasi
Jalankan program dengan perintah berikut :
npm run serve
Hasilnya sbb :

Klik Link Donasi

Isi Form lalu ketik tombol Donasi

Struktur Direktori
