VueJs-#08c Vuex Module & Helper

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>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td>&nbsp;</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

Donasi

Isi Form lalu ketik tombol Donasi

After Insert

Struktur Direktori

Struktur Direktori

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>