Membuat Aplikasi Ekspedisi NuxtJS #3: CRUD Data Users Bagian 2

Membuat Aplikasi Ekspedisi NuxtJS #3: CRUD Data Users Bagian 2

Pendahuluan

Memanipulasi data user dengan mengimplementasikan teknik CRUD antara NuxtJS sebagai frontend dan Lumen sebagai backend. Seri membuat aplikasi ekspedisi episode NuxtJS kali ini akan membahas bagaimana menerapkan fungsi update data dan hapus data.

Hal lain yang akan menjadi topik pembahasan adalah bagaimana membuat fitur pencarian dan pagination di NuxtJS dengan memanfaatkan API yang sudah ada. Seri ini adalah seri penutup dalam mengenali dan memahami teknik CRUD di NuxtJS.

Baca Juga: Membuat Aplikasi Ekspedisi NuxtJS #3: CRUD Data Users Bagian 2

Fitur Pencarian & Pagination

Sebelum memulai untuk mengerjakan fitur ini, alangkah baiknya untuk membaca artikel membuat aplikasi ekspedisi episode Lumen seri ke-4 dan lebih tepatnya pada bagian issues. Hal ini disebabkan adanya update materi karena pada API kita belum menyediakan fitur pencarian data.

Jika API-nya sudah tersedia, maka hal yang akan kita kerjakan adalah membuat tombol yang akan mengarahkan ke halaman edit dan hapus data. Selain itu, kita juga akan membuat input-an untuk melakukan pencarian dan bagian terakhir adalah fungsi untuk pagination. Buka file /pages/users/index.vue dan modifikasi menjadi

<template>
    <div class="container-fluid">
        <div class="card">
            <div class="card-header">
                <h4 class="card-title">
                    List Users
                    <nuxt-link class="btn btn-primary float-right btn-sm" :to="{name: 'users-add'}">Add New</nuxt-link>
                </h4>
            </div>
            <div class="card-body row">
                <div class="col-md-4 offset-md-8 mb-4">
                  	<!-- FUNGSI PENCARIAN INI AKAN BERFUNGSI KETIKA TOMBOL ENTER DITEKAN -->
                    <input type="text" placeholder="Search" v-model="search" @keypress.enter="findUser" class="form-control">
                  
                </div>
                <div class="col-md-12">
                    <b-table striped hover :items="users.data" :fields="fields" show-empty responsive>
                        <template v-slot:cell(name)="row">
                            <p><strong>{{ row.item.name }}</strong></p>
                            <p>
                                ID: <span class="badge badge-success">{{ row.item.identity_id }}</span> <br>
                                Gender: <span class="badge badge-success">{{ row.item.gender == 1 ? 'Male':'Female' }}</span>
                            </p>
                        </template>
                      
                      	<!-- MENAMPILKAN TOMBOL EDIT DAN HAPUS --> 
                        <template v-slot:cell(actions)="row">
                            <!-- TOMBOL EDIT AKAN DIARAHKAN KE HALAMAN BARU --> 
                            <nuxt-link :to="{name: 'users-edit-id', params: {id: row.item.id}}" :key="'edit'+row.index" class="btn btn-warning btn-sm">Edit</nuxt-link>
                            <!-- TOMBOL HAPUS AKAN MEMBUKA MODAL KONFIRMASI --> 
                            <button class="btn btn-danger btn-sm" @click="openDeleteModal(row)">Delete</button>
                        </template>
                        <!-- MENAMPILKAN TOMBOL EDIT DAN HAPUS --> 
                      
                    </b-table>
                  
                    <!-- MENAMPILKAN PAGINASI DATA USER, DIMANA V-MODELNYA BERDASARKAN PAGE YANG SEDANG AKTIF --> 
                    <b-pagination
                        align="right"
                        v-model="users.current_page"
                        :total-rows="users.total"
                        :per-page="users.per_page"
                        @change="changePage"
                        aria-controls="my-table"
                    ></b-pagination>
                </div>
              
              	<!-- MODAL AKAN DITEMPATKAN DISINI -->
            </div>
        </div>
    </div>
</template>

<script>
import { mapActions, mapState, mapMutations } from 'vuex'
export default {
    async asyncData({store}) {
        await Promise.all([
            store.dispatch('user/getUsersData')
        ])
        return
    },
    data() {
        return {
            fields: ['name', 'address', 'email', 'phone_number', 'actions'], 
            items: [],
            deleteModal: false, 
            user_selected: null, //MENGHANDLE DATA USER YANG AKAN DIHAPUS
            search: '' //MENGHANDLE VALUE PENCARIAN
        }
    },
    computed: {
        ...mapState('user', {
            users: state => state.users,
            page: state => state.page //AMBIL DATA PAGE YANG SEDANG AKTIF SAAT INI
        })
    },
    watch: {
        //JIKA VALUE PAGE BERUBAH
        page() {
            //MAKA REQUEST DATA BARU
            this.getUsersData(this.search)
        }  
    },
    methods: {
        ...mapActions('user', ['getUsersData', 'destroyUsersData']),
        ...mapMutations('user', ['SET_PAGE']),
        //JIKA TOMBOL DELETE DITEKAN, MAKA FUNGSI INI AKAN DIJALANKAN
        openDeleteModal(row) {
            this.user_selected = row.item //KITA SET USER YANG AKAN DIHAPUS
            this.deleteModal = true //BUKA MODAL KONFIRMASI
        },
        //JIKA TOMBOL DELETE PADA MODAL DITEKAN, MAKA FUNGSI INI AKAN DIJALANKAN
        deleteDataUser() {
            
        },
        //JIKA PADA FORM PENCARIAN DITEKAN ENTER, MAKA FUNGSI INI AKAN DIJALANKAN
        findUser() {
            //LAKUKAN PEMANGGILAN KE API UNTUK MENDAPATKAN DATA BERDASARKAN PENCARIAN
            this.getUsersData(this.search)
        },
        //JIKA PAGINATION DIKLIK, MAKA AKAN MENGESET VALUE PAGE YANG SEDANG AKTIF
        changePage(val) {
            this.SET_PAGE(val)
        }
    }
}
</script>

Ada beberapa bagian yang perlu didefinisikan pada module user, diantaranya adalah mutation SET_PAGE, state page, dan memodifikasi actions untuk mengambil data users. Adapun skema yang diinginkan dari request API untuk mendapatkan data users adalah setiap kali terjadi request, kita mengirimkan dua buah parameter, yakni parameter untuk pencarian dan page yang sedang aktif. Buka file store/user.js dan modifikasi menjadi

export const state = () => ({
    users: [],
    errors: [],
    page: 1 //DEFAULT PAGE YANG AKTIF ADALAH 1
})

export const mutations = {
    SET_USER_DATA(state, payload) {
        state.users = payload
    },
    SET_ERRORS(state, payload) {
        state.errors = payload
    },
    //MUTATION UNTUK MENGUBAH VALUE DARI STATE PAGE
    SET_PAGE(state, payload) {
        state.page = payload
    }
}

export const actions = {
    getUsersData({ commit, state }, payload) {
        let search = payload ? payload:''
        return new Promise((resolve, reject) => {
            //KIRIM REQUEST KE API, DENGAN MENGIRMKAN PARAMETER Q DAN PAGE
            //DIMANA Q ADALAH PENCARIAN DAN PAGE ADALAH AKTIF PAGE YANG SEDANG DIAKSES
            this.$axios.get(`/users?q=${search}&page=${state.page}`).then((response) => {
                commit('SET_USER_DATA', response.data.data) //JIKA BERHASIL, SET DATA BARU 
                resolve()
            })
        })
    },
    storeUsersData({ dispatch, commit }, payload) {
        return new Promise((resolve, reject) => {
            this.$axios.post('/users', payload)
            .then(() => {
                dispatch('getUsersData')
                resolve()
            })
            .catch((error) => {
                commit('SET_ERRORS', error.response.data)
            })
        })
    },
}

Sampai pada tahap ini, fitur pencarian dan pagination sudah berhasil kita selesaikan. Adapun tampilan yang akan kita peroleh kurang lebih seperti gambar berikut

aplikasi ekspedisi nuxtjs - search and pagination

Fitur Delete Data User

Pada sub-section sebelumnya, kita sudah membuat tombol untuk menghapus data dengan skema yang diinginkan adalah membuka modal sebagai popup konfirmasi sebelum benar-benar dihapus dari database. Maka buka kembali file /pages/users/index.vue dan tambahkan code modal berikut

<!-- MODAL INI AKAN DIBUKA, JIKA deleteModal bernilai true -->
<b-modal v-model="deleteModal" title="Delete Data User">
    <!-- TAMPILKAN DATA USER YANG AKAN DIHAPUS -->
    <p>Kamu yakin ingin menghapus data: <code>{{ user_selected ? user_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>
          	<!-- JIKA TOMBOL INI DITEKAN, MAKA AKAN MENJALANKAN FUNGSI deleteDataUser -->
            <b-button
                variant="primary"
                size="sm"
                @click="deleteDataUser"
            >
                Delete
            </b-button>
        </div>
    </template>
</b-modal>

Note: Replace komentar yang sudah saya tandai di atas.

Masih dengan file yang sama, modifikasi method deleteDataUser menjadi

deleteDataUser() {
    //JALANKAN ACTIONS YANG BERISI PERINTAH UNTUK HIT KE API
    //DENGAN MENGIRIMKAN ID USER YANG AKAN DIHAPUS
    this.destroyUsersData(this.user_selected.id).then(() => {
        //JIKA BERHASIL, TUTUP MODAL DAN BERSIHKAN SELECTED USER
        this.deleteModal = false
        this.user_selected = null
    })
},

Sebagai penutup, tambahkan code berikut pada bagian actions dari file store/user.js

destroyUsersData({ dispatch, commit }, payload) {
    return new Promise((resolve, reject) => {
        this.$axios.delete(`/users/${payload}`)
        .then(() => {
            dispatch('getUsersData')
            resolve()
        })
        .catch((error) => {
            commit('SET_ERRORS', error.response.data)
        })
    })
},

aplikasi ekspedisi nuxtjs - show modal

Fitur Update Data User

Tombol edit data sudah tersedia, dimana tombol ini akan mengarahkan kita pada sebuah halaman baru yang akan menampilkan form beserta data user yang akan diperbaharui. Ada 3 langkah yang akan kita lakukan, yakni langkah pertama adalah menampilkan form, get data yang akan di-edit dan yang terakhir adalah memperbaharui data.

Langkah pertama, buat folder _id di dalam folder /pages/users/edit, dimana strukturnya akan menjadi pages > users > edit > _id. Kemudian buat file index.vue di dalam folder _id tersebut dan masukkan code berikut

<template>
    <div class="container-fluid">
        <div class="card mb-3">
            <div class="card-header">
                <h4 class="card-title"></h4>
            </div>
            <div class="card-body">
                <app-user-form />
            </div>
        </div>
    </div>
</template>
<script>
import UserForm from '@/components/form/User.vue' 
export default {
    //FUNGSI INI AKAN DIJALANKAN KETIKA HALAMAN EDIT DI LOAD
    async asyncData({store, params}) {
        await Promise.all([
            //DAN AKAN MEMINTA DATA YANG AKAN DIEDIT BERDASARKAN ID USER
            store.dispatch('user/getData', params.id)
        ])
        return
    },
    components: {
        'app-user-form': UserForm
    }
}
</script>

Form yang akan digunakan adalah form yang sama ketika kita melakukan penambahan data, dimana form ini sudah dipisahkan ke dalam component tersendiri. Buka file /components/form/User.vue dan tambahkan beberapa part berikut. Bagian pertama, tambahkan tag ini setelah tombol save.

<nuxt-link :to="{name: 'users'}" class="btn btn-secondary btn-sm">Back</nuxt-link>

Informasi perubahan password setelah tag input password

<p v-if="$route.name == 'users-edit-id'">Leave blank if you don't change your password</p>

Tambahkan juga created untuk memasukkan data user yang diperoleh dari server ke dalam variable

created() {
    //FUNGSI INI HANYA AKAN KERJAKAN, APABILA HALAMAN YANG DILOAD ADALAH EDIT USER
    if (this.$route.name == 'users-edit-id') {
        this.users = {
            name: this.user.name,
            identity_id: this.user.identity_id,
            gender: this.user.gender,
            address: this.user.address,
            email: this.user.email,
            password: '',
            phone_number: this.user.phone_number,
            role: this.user.role,
            status: this.user.status
        }
    }
},

Bagian terakhir adalah state user yang akan di-edit dan membuat fungsi submit untuk update data user.

computed: {
    ...mapState('user', {
        user: state => state.data,
        errors: state => state.errors
    })
},
methods: {
    ...mapActions('user', ['storeUsersData', 'updateUserData']),
    submit() {
        //JIKA PAGE YANG DILOAD ADALAH EDIT USER
        if (this.$route.name == 'users-edit-id') {
            //MAKA ASSIGN ID KE DALAM DATA USER
            let data = Object.assign({id: this.$route.params.id}, this.users)
            //KEMUDIAN KIRIM REQUEST KE SERVER
            this.updateUserData(data).then((this.$router.push({name: 'users'})))
        } else {
            //SELAIN ITU MAKA FUNGSI ADD DATA YANG AKAN DIJALAKAN
            this.storeUsersData(this.users).then(() => this.$router.push({name: 'users'}))
        }
    }
}

Tiba saatnya untuk meng-handle fungsi untuk melakukan hit ke API dan menampung state yang akan di-edit. Buka file store/user.js dan tambahkan state

export const state = () => ({
    users: [],
    data: [], //TAMBAHKAN STATE INI
    errors: [],
    page: 1
})

Kemudian buat mutation untuk memanipulasi state data

SET_DATA(state, payload) {
    state.data = payload
},

Lalu tambahkan kedua actions berikut ini ke dalam file yang sama

getData({ commit }, payload) {
    return new Promise((resolve, reject) => {
        //KIRIM REQUEST KE SERVER UNTUK MENGAMBIL DATA USER
        this.$axios.get(`/users/${payload}`).then((response) => {
            commit('SET_DATA', response.data.data) //SET DATA YANG DITERIMA KE DALAM STATE
            resolve()
        })
    })
},
updateUserData({ dispatch, commit }, payload) {
    return new Promise((resolve, reject) => {
        //KIRIM PERMINTAAN UNTUK MENGUPDATE DATA BERDASARKAN ID USER
        this.$axios.put(`/users/${payload.id}`, payload)
        .then(() => {
            dispatch('getUsersData') //AMBIL DATA USER YANG BARU 
            resolve()
        })
        .catch((error) => {
            commit('SET_ERRORS', error.response.data)
        })
    })
},

Baca Juga: Membuat Aplikasi Ekspedisi NuxtJS #2: Authentication From Scratch

Kesimpulan

Menutup seri CRUD data user, dimana kita telah belajar bagaimana mengedit dan menghapus data user yang sudah ada. Selain itu, hal baru yang kita pelajari adalah cara membuat pagination, melakukan pencarian data dan lain sebagainya.

Adapun dokumentasi code dari artikel ini bisa ditemukan di Github.

Category:
Share:

Comments