Membuat Aplikasi Ekspedisi NuxtJS #4: Manage Categories

Membuat Aplikasi Ekspedisi NuxtJS #4: Manage Categories

Pendahuluan

Mengelola data kategori akan menjadi bahasan utama kita dalam materi Membuat Aplikasi Ekspedisi NuxtJS seri ke-4. Fitur yang akan dikerjakan tidak jauh berbeda dengan materi sebelumnya yang membahas tentang data users. Sebab bahasan kita meliputi hal yang sama, yakni menampilkan data, menghapus, menambahkan, dan memperbaharui data.

Fitur lainnya yang masih terkait dengan materi ini adalah pencarian dan pagination. Rangkaian fitur ini akan kita hubungkan satu persatu menjadi sebuah module kategori. Sebelum melanjutkan, pastikan kamu sudah membaca materi episode Lumen yang membahas tentang cara API untuk data kategori.

Baca Juga: Cara Upload File ke Amazon S3 Menggunakan Laravel 7

Show & Delete Data Category

Langkah pertama yang akan dikerjakan adalah fungsi untuk menampilkan data kategori, jadi buat folder baru dengan nama categories di dalam folder pages. Kemudian buat file index.vue dan letakkan ke dalam folder categories yang baru saja dibuat dan tambahkan code berikut.

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-4">
              
                <!-- FORM ADD DATA -->
              
            </div>
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">
                        <h4 class="card-title">
                            List Categories
                        </h4>
                    </div>
                    <div class="card-body row">
                        <div class="col-md-4 offset-md-8 mb-4">
                            <input type="text" placeholder="Search" v-model="search" @keypress.enter="findCategory" class="form-control">
                        </div>
                        <div class="col-md-12">
                            <b-table striped hover :items="categories.data" :fields="fields" show-empty responsive>
                                <template v-slot:cell(actions)="row">
                                    <button class="btn btn-warning btn-sm" @click="openEditModal(row)">Edit</button>
                                    <button class="btn btn-danger btn-sm" @click="openDeleteModal(row)">Delete</button>
                                </template>
                            </b-table>
                            <b-pagination
                                align="right"
                                v-model="categories.current_page"
                                :total-rows="categories.total"
                                :per-page="categories.per_page"
                                @change="changePage"
                                aria-controls="my-table"
                            ></b-pagination>
                        </div>

                        <!-- DELETE MODAL -->

                        <!-- EDIT MODAL -->
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import { mapActions, mapState, mapMutations } from 'vuex'

export default {
    //FUNGSI INI AKAN BERJALAN SEBELUM COMPONENT DILOAD
    async asyncData({store}) {
        await Promise.all([
            //KITA MELOAD FUNGSI GETCATEGORIESDATA UNTUK MENGAMBIL DATA KATEGORI DARI API
            store.dispatch('category/getCategoriesData')
        ])
        return
    },
    data() {
        return {
            fields: ['name', 'description', 'actions'], 
            items: [],
            search: ''
        }
    },
    computed: {
        //LOAD STATE CATEGORIES DAN PAGE YANG AKTIF
        ...mapState('category', {
            categories: state => state.categories,
            page: state => state.page
        })
    },
    watch: {
        //KETIKA VALUE DARI STATE PAGE BERUBAH
        page() {
            //MAKA KITA REQUEST DATA BARU
            this.getCategoriesData(this.search)
        }  
    },
    methods: {
        //LOAD ACTIONS DARI MODULE CATEGORY
        ...mapActions('category', ['getCategoriesData']),
        ...mapMutations('category', ['SET_PAGE']),
        openDeleteModal(row) {
            
        },
        openEditModal(row) {
            
        },
        findCategory() {
            this.getCategoriesData(this.search)
        },
        changePage(val) {
            this.SET_PAGE(val)
        }
    }
}
</script>

Note: ada 3 buah komentar yang perlu diperhatikan, yakni <!-- EDIT MODAL -->, <!-- DELETE MODAL --> & bagian terakhir adalah <!-- FORM ADD DATA -->. Ketiga bagian ini akan di-replace nantinya dengan masing-masing code yang dibutuhkan apabila ada instruksi replace.

Bagian selanjutnya adalah menyediakan module Vuex, dalam hal ini adalah category. Buat file baru bernama category.js di dalam folder store.

export const state = () => ({
    categories: [], //STATE UNTUK MENYIMPAN DATA KATEGORI
    errors: [],
    page: 1
})

export const mutations = {
    //MUTATION UNTUK MENGUBAH VALUE DARI STATE KATEGORI
    SET_CATEGORY_DATA(state, payload) {
        state.categories = payload
    },
    SET_ERRORS(state, payload) {
        state.errors = payload
    },
    SET_PAGE(state, payload) {
        state.page = payload
    }
}

export const actions = {
    //ACTIONS UNTUK MELAKUKAN REQUEST KE API
    getCategoriesData({ commit, state }, payload) {
        let search = payload ? payload:''
        return new Promise((resolve, reject) => {
            //MENGGUNAKAN AXIOS DENGAN TARGET /CATEGORIES
            this.$axios.get(`/categories?q=${search}&page=${state.page}`).then((response) => {
                //JIKA PROSESNYA SELESAI, MAKA DATA YANG DITERIMA DARI API
                //AKAN DISIMPAN KE DALAM STATE CATEGORIES
                commit('SET_CATEGORY_DATA', response.data.data)
                resolve()
            })
        })
    }
}

Note: Perlu diketahui bahwa NuxtJS akan secara otomatis mengenali module Vuex apabila kita letakkan di dalam folder store, sehingga tidak perlu untuk mendefinisikannya secara manual.

Agar halaman Category bisa diakses melalui sidebar menu, buka file components/layouts/Sidebar.vue dan tambahkan code berikut tepat di atas tag menu Manage Users.

<li class="nav-item">
    <nuxt-link class="nav-link" :to="{name: 'categories'}">
        <i class="fas fa-fw fa-tag"></i>
        <span>Manage Categories</span>
    </nuxt-link>
</li>

Adapun hasil yang akan kita peroleh akan terlihat seperti gambar berikut

aplikasi ekspedisi nuxtjs - data categories

Fungsi selanjutnya sekaligus penutup dari sub-category ini adalah fungsi untuk menghapus data. Buka file /categories/index.vue dan replace komentar <!-- DELETE MODAL --> dengan code berikut

<b-modal v-model="deleteModal" title="Delete Data Category">
    <p>Kamu yakin ingin menghapus data: <code>{{ category_selected ? category_selected.name:'' }}</code>?</p>
    <template v-slot:modal-footer>
        <div class="w-100 float-right">
            <b-button
                variant="secondary"
                size="sm"
                @click="deleteModal=false"
            >
                Close
            </b-button>
            <b-button
                variant="primary"
                size="sm"
                @click="deleteDataCategory"
            >
                Delete
            </b-button>
        </div>
    </template>
</b-modal>

Masih dengan file yang sama, tambahkan code dibawah ini tepat di dalam property data().

data() {
    return {
        fields: ['name', 'description', 'actions'], 
        items: [],
        search: '',
        //TAMBAHKAN CODE INI
        deleteModal: false,
        category_selected: null,
    }
},

Kemudian modifikasi mapActions yang ada dengan menambahkan destroyCategoryData

...mapActions('category', ['getCategoriesData', 'destroyCategoryData']),

Lalu modifikasi methods openDeleteModal() menjadi

openDeleteModal(row) {
    this.category_selected = row.item
    this.deleteModal = true
},

Bagian terakhir adalah membuat methods deleteDataCategory()

deleteDataCategory() {
    this.destroyCategoryData(this.category_selected.id).then(() => {
        this.deleteModal = false
        this.category_selected = null
    })
},

Buka file category.js dan tambahkan method di bawah ini untuk melengkapi actions yang akan melakukan request ke API.

destroyCategoryData({ dispatch, commit }, payload) {
    return new Promise((resolve, reject) => {
        //KIRIM PERMINTAAN KE API UNTUK MENGHAPUS DATA
        this.$axios.delete(`/categories/${payload}`)
        .then(() => {
            //LOAD DATA TERBARU
            dispatch('getCategoriesData')
            resolve()
        })
        .catch((error) => {
            commit('SET_ERRORS', error.response.data)
        })
    })
},

Modal untuk delete data akan terlihat seperti gambar berikut

aplikasi ekspedisi nuxtjs - delete categories

Add New & Edit Category

Module pada sub-chapter ini berkaitan dengan fungsi manipulasi data, dimana bagian pertama yang akan kita kerjakan ada form untuk menambahkan data. Replace komentar <!-- FORM ADD DATA --> dengan code berikut

<div class="card">
    <div class="card-header">
        <h4 class="card-title">Add New</h4>
    </div>
    <div class="card-body">
        <category-form ref="form" />
        <button class="btn btn-primary btn-sm" @click="submit">Save</button>
    </div>
</div>

Ada yang menarik dari code di atas, dimana kita melihat sebuah custom tag category-form. Maka untuk membuat tag ini, kita memerlukan sebuah component yang berisi form input. Buat file Category.vue di dalam folder components/form dan tambahkan code berikut

<template>
    <div>
        <div class="form-group">
            <label for="">Category</label>
            <input type="text" class="form-control" v-model="category.name">
        </div>
        <div class="form-group">
            <label for="">Description</label>
            <input type="text" class="form-control" v-model="category.description">
        </div>
    </div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
    data() {
        return {
            category: {
                name: '',
                description: ''
            },
        }
    },
    methods: {
        ...mapActions('category', ['storeCategoryData']),
        submit() {
            this.storeCategoryData(this.category).then(() => {
                this.clearForm()
            })
        },
        clearForm() {
            this.category = {
                name: '',
                description: ''
            }
        }
    }
}
</script>

Kembali ke file /categories/index.vue, tambahkan attribute dibawah ini untuk custom tags.

components: {
    'category-form': Category
},

Jangan lupa untuk meng-import component Category

import Category from '@/components/form/Category.vue'

Masih di dalam file yang sama, tambahkan method submit untuk meng-handle proses penyimpanan file

submit() {
    this.$refs.form.submit()
},

Penjelasan: this.$refs.form.submit() berarti kita menjalankan fungsi submit yang ada di dalam component Category. Cara mengetahui bahwa yang dijalankan adalah dari component Category darimana? Perhatikan pada tag <category-form ref="form" />, kita mendefinisikan ref="form". Maka key form ini akan digunakan sebagai penghubung ke component tersebut.

Bagian terakhir adalah membuat actions dari module Vuex untuk melakukan request ke server. Buka file category.js dan tambahkan code berikut

storeCategoryData({ dispatch, commit }, payload) {
    return new Promise((resolve, reject) => {
        this.$axios.post('/categories', payload)
        .then(() => {
            dispatch('getCategoriesData')
            resolve()
        })
        .catch((error) => {
            commit('SET_ERRORS', error.response.data)
        })
    })
},

aplikasi ekspedisi nuxtjs - add new category

Fitur berikutnya adalah fungsi untuk memperbaharui data kategori. Buka file /categories/index.vue dan replace komentar <!-- Edit Modal --> dengan code berikut

<b-modal v-model="editModal" title="Edit Category">
    <category-form ref="formEdit" :selected="category_selected" />
    <template v-slot:modal-footer>
        <div class="w-100 float-right">
            <b-button
                variant="secondary"
                size="sm"
                @click="deleteModal=false"
            >
                Close
            </b-button>
            <b-button
                variant="primary"
                size="sm"
                @click="updateCategory"
            >
                Update
            </b-button>
        </div>
    </template>
</b-modal>

Pada property data() dengan file yang sama, tambahkan variable berikut

editModal: false,

Modifikasi methods openEditModal() menjadi

openEditModal(row) {
    this.category_selected = row.item
    this.editModal = true
},
closeEditModal(row) {
    this.category_selected = null
    this.editModal = false
},

Tambahkan juga method baru untuk meng-handle update data.

updateCategory() {
    this.$refs.formEdit.update().then(() => this.closeEditModal())
}

Penjelasan: metode yang digunakan sama saja, jadi kita membuat ref key untuk kemudian menjalankan fungsi update yang ada pada component Category.

Buka file Category.vue, dan tambahkan property created() & props untuk menampung data

created() {
    if (this.selected) {
        this.category = {
            id: this.selected.id,
            name: this.selected.name,
            description: this.selected.description,
        }
    }
},
props: {
    selected: {
        type: Object,
        default: null
    }
},

Pada bagian methods, modifikasi mapActions yang ada menjadi

...mapActions('category', ['storeCategoryData', 'updateCategoryData']),

Kemudian tambahkan methods berikut

update() {
    return new Promise((resolve, reject) => {
        // let data = Object.assign(this.category, {id: this.selected.id})
        this.updateCategoryData(this.category).then(() => resolve())
    })
},

Sebagai penutup dari fitur ini adalah dengan membuat actions Vuex dari module category, buka file category.js dan tambahkan code berikut

updateCategoryData({ dispatch, commit }, payload) {
    return new Promise((resolve, reject) => {
        //MEMBUAT REQUEST KE SERVER UNTUK MEMPERBAHARUI DATA
        this.$axios.put(`/categories/${payload.id}`, payload)
        .then(() => {
            //MENGAMBIL DATA CATEGORY YANG TERBARU
            dispatch('getCategoriesData')
            resolve()
        })
        .catch((error) => {
            commit('SET_ERRORS', error.response.data)
        })
    })
},

Baca Juga: Cara Membuat Sistem Komentar Dengan Laravel 7

Kesimpulan

Mengulang kembali materi terkait CRUD, dimana perbedaan yang baru saja kita pelajari adalah memindahkan proses input dan edit data ke dalam halaman. Selain itu kita juga belajar bagaimana membuat reusable component untuk form input.

Adapun dokumentasi code dari artikel ini bisa dilihat di Github.

 

Category:
Share:

Comments