Tutorial Vuex #3: Module & Helpers

Tutorial Vuex #3: Module & Helpers

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?

Kadang kita mengenal lelucon, "Ketika codingan yang kita buat, hanya kita dan dan Tuhan yang tahu. Dan setelah beberapa saat, hanya Tuhan saja yang tahu.".

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, Client 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.

Baca Juga: Tutorial Vuex #1: Manajemen State

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:

vue create donasi

Kemudian install library Vuex

npm install vuex --save

Kita akan membuat 3 buah component, yakni: Donatur.vue (untuk meng-handle list donatur), Clients.Vue (untuk meng-handle jenis bantuan) dan Transaksi.vue (untuk mencatat transaksi donasi yang telah dilakukan). Adapun hasil yang akan kita capai adalah sebagai berikut:

Cara membuat modules vuex

Buat file Donatur.vue didalam folder src/components:

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

Penjelasan: Yang perlu diperhatikan adalah computed property yang ada, dimana data tersebut berasal dari module Vuex yang bernama donatur. Selanjutnya module ini akan kita buat pada sub topik selanjutnya. So, cara mengambil data state dari Vuex adalah this.$store.state kemudian diikuti dengan nama state (jika tidak didalam module) atau dengan nama module kemudian nama state (jika state tersebut berada didalam module).

Component kedua adalah Clients.vue, tempatkan didalam folder src/components:

<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: {
            listBantuan() {
                return this.$store.state.clients.listBantuan
            }
        }
    }
</script>

Note: Metode yang digunakan sama dengan component sebelumnya.

Component trakhir adalah Transaksi.vue, letakkan didalam folder yang sama dengan component sebelumnya:

<template>
    <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>
                
            </tbody>
        </table>
    </div>
</template>
<script>
    export default {
        computed: {
            
        }
    }
</script>

Note: Computed property sengaja kita kosongkan, karena akan menggunakan pemanggilan state dengan helpers.

Buka file public/index.html, kemudian tambahkan cdn Bootstrap karena kita akan menggunakan style dari Bootstrap:

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

Buka file src/App.vue, kemudian modifikasi menjadi:

<template>
	<div id="app">
		<div class="container" style="padding-top: 20px">
			<div class="row">
				<div class="col-md-6">
					<div class="card">
						<div class="card-header">
							<h3 class="card-title">Form Donasi</h3>
						</div>
						<div class="card-body">
							<list-donatur @selectedDonatur="selectedDonatur" />
							<lokasi-bencana @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"
									@click="submitDonasi"
									:disabled="isLoading">
									{{ isLoading ? 'Loading...':'Donasi!' }}
								</button>
							</div>
						</div>
					</div>
				</div>
				<div class="col-md-6">
					<div class="card">
						<div class="card-header">
							<h3 class="card-title">Donasi Terkumpul</h3>
						</div>
						<div class="card-body">
							<list-transaksi />
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
</template>

<script>
	import Donatur from './components/Donatur.vue'
	import Clients from './components/Clients.vue'
	import Transaksi from './components/Transaksi.vue'

	export default {
		data() {
			return {
				donatur: '',
				clients: '',
				jumlah: 0
			}
		},
		computed: {
			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.clients = val
			},
			submitDonasi() {
				//AKAN KITA MODIFIKASI SELANJUTNYA
			}
		},
		components: {
			'list-donatur': Donatur,
			'lokasi-bencana': Clients,
			'list-transaksi': Transaksi
		}
	}
</script>

Note: Ketiga component tersebut kita import untuk digunakan pada form yang telah disediakan. Untuk penjelasan masing-masing methods yang terakait sudah saya sematkan didalam coding diatas.

Buat file store.js didalam folder src kemudian tambahkan code berikut:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        isLoading: false
    },
    modules: {
        // MODULE AKAN DITEMPATKAN DISINI
    }
})

Terakhir buka file main.js, kemudian modifikasi menjadi:

import Vue from 'vue'
import App from './App.vue'
import store from './store' //IMPORT STORE.JS

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store // USE store
}).$mount('#app')

Baca Juga: Tutorial Vuex #2: Mutations & Actions

Membuat Module Vuex & Menggunakan Helpers

Module memungkinkan kita untuk memecah bagian dari aplikasi kita agar dapat di-handle dengan mudah, sebab apabila digabungkan dalam satu file akan membentuk kumpulan code yang cukup besar dan menyulitkan kita untuk mencari bagian bagian yang akan dimodifikasi kemudian hari.

Buat sebuah folder dengan nama modules (baca: nama folder boleh beda, bebas.) didalam folder src untuk menampung file module yang akan dibuat selanjutnya. Terdapat 3 buah component, dimana masing-masing component ini pada case kali ini akan kita kelompokkan menjadi 3 buah modules juga, yakni: donatur, clients dan transaksi.

Pertama, buat file donatur.js didalam folder src/modules, kemudian masukkan code berikut:

const donatur = {
    namespaced: true,
    state: {
        listDonatur: [
            { name: 'Riski Amelia' },
            { name: 'Ima Sumadir' },
            { name: 'Apri Yunita' },
            { name: 'Tuti Winarti' }
        ]
    },
    mutations: {
        // NONE
    },
    actions: {
        // NONE
    }
}

export default donatur

Penjelasan: Terdapat sebuah state listDonatur yang memiliki data default sebanyak 4 buah object. Dimana data ini akan ditampilkan pada component Donatur.vue yang telah dibuat sebelumnya.

File berikutnya adalah client.js yang diletakkan didalam folder src/modules, kemudian tambahkan code berikut:

const clients = {
    namespaced: true,
    state: {
        listBantuan: [
            { name: 'Gempa Lombok', },
            { name: 'Beasiswa Pendidikan' },
            { name: 'Banjir Bandang' },
            { name: 'Tanggungan Kesehatan' }
        ]
    },
    mutations: {
        // NONE
    },
    actions: {
        // NONE
    }
}

export default clients

Note: Kegunaannya sama dengan module sebelumnya.

Terakhir, buat file transaksi.js didalam folder src/modules:

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

Agar ketiga modules tersebut dapat digunakan, maka kita harus mendefinisikannya pada root Vuex, dalam hal ini file store.js, buka file tersebut kemudian modifikasi menjadi:

import Vue from 'vue'
import Vuex from 'vuex'
//IMPORT KETIGA MODULES
import donatur from './modules/donatur'
import clients from './modules/clients'
import transaksi from './modules/transaksi'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        isLoading: false
    },
    modules: {
        //DEFINISIKAN KETIGA MODULE TERSEBUT
        donatur,
        clients,
        transaksi
    }
})

Pada form input donasi kita memiliki sebuah tombol yang memicu methods submitDonasi() ketika di-klik. Dimana methods tersebut masih kita biarkan kosong pada sub topik sebelumnya. So, buka file App.vue kemudian modifikasi methods tersebut menjadi:

submitDonasi() {
    // dispatch BERARTI MEMANGGIL FUNGSI YANG ADA DI ACTION
    // DIMANA FUNGSI TERSEBUT BERNAMA save_donasi
    // DAN BERADA DIDALAM MODULE transaksi
    // JADI CARA MEMANGGILNYA ADALAH namamodule/namafungsi
    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.clients,
        jumlah: this.jumlah
    })
}

Sampai pada tahap ini kita sudah berhasilkan membuat form untuk menambahkan data baru kedalam array listTransaksi untuk menampung data tersebut. Akan tetapi, listTransaksi belum dapat digunakan oleh component Transaksi.vue karena data tersebut belum didefinisikan pada component terkait.

Yap! Kita akan menggunakan computed property untuk menggunakan state dari module transaksi. Hanya saja, jika dua component lainnya yakni: Donatur.vue dan Client.vue menggunakan cara this.$store.state.namamodule.namastate, maka pada component Transaksi.vue akan menggunakan helpers mapState.

Buka file Transaksi.vue, kemudian modifikasi menjadi:

<template>
    <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>{{ index+1 }}</td>
                    <td>{{ row.donatur }}</td>
                    <td>{{ row.bantuan }}</td>
                    <td>{{ row.jumlah }}</td>
                </tr>
            </tbody>
        </table>
    </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
            })
        }
    }
</script>

Sampai pada tahap ini, aplikasi yang kita inginkan sudah berjalan sebagaimana mestinya. Jalankan command npm run serve.

Sebagai tambahan karena tidak sempat digunakan pada codingan diatas, maka untuk penggunaan helpers mapActions dapat kamu lakukan dengan cara:

methods: {
    ...mapActions('namamodule', ['namafungsi']), //HELPERS ACTIONS YANG BERADA DALAM MODULE
    ...mapActions(['namafungsi']), //HELPERS ACTION YANG TIDAK BERADA DALAM MODULE
    simpan() {
        this.namafungsi() //CARA MENGGUNAKAN ACTIONS YANG SUDAH DIPANGGIL MELALUI HELPERS
    }
}

Kesimpulan

Vuex memungkin codingan yang kamu menjadi lebih terstruktur sehinga lebih mudah dalam mengelolanya baik saat proses development maupun ketika ada pengembangan dikemudian hari. Lengkap sudah ketiga seri ini sebagai modal dasar dalam memahami bagaimana cara menggunakan Vuex.

Bermodalkan pemahaman bagaimana cara mendefinisikan state, mutations, dan action serta cara menggunakan ketiganya baik didalam modules maupun tidak, sudah menjadi modal yang cukup kuat untuk diimplementasikan pada project yang sedang kamu kerjakan. Seiringin berjalannya waktu kamu akan menemukan best practice dalam menggunakan Vuex.

Dokumentasi dari code diatas dapat kamu lihat di Github.

Category:
Share:

Comments