Tutorial Vue 3 TypeScript #5: Edit dan Update Data Dengan Rest API


Tutorial Vue 3 TypeScript #5: Edit dan Update Data Dengan Rest API

Halo teman-teman semuanya, pada artikel sebelumnya kita telah belajar bagaimana cara melakukan proses insert dan upload gambar di Vue 3 TypeScript dan pada artikel kali ini kita semua akan belajar bagaimana cara membuat proses edit dan update data di Vue 3 TypeScript.

Langkah 1 - Edit dan Update Data di Vue 3

Silahkan teman-teman buat file baru dengan nama edit.vue di dalam folder src/views/products, kemudian masukkan kode berikut ini di dalamnya.

src/views/products/edit.vue

<script setup lang="ts">

//import ref dan onMounted dari vue
import { ref, onMounted } from "vue";

//import useRoute dan useRouter dari vue-router
import { useRoute, useRouter } from "vue-router";

//import Api dari folder api
import Api from "../../api";

// Interface Errors
interface Errors {
    image?: string[];
    title?: string[];
    description?: string[];
    price?: string[];
    stock?: string[];
}

// State untuk form
const image = ref<File | null>(null);
const title = ref("");
const description = ref("");
const price = ref("");
const stock = ref("");

// State errors
const errors = ref<Errors>({});

//initiate route and router
const route = useRoute();
const router = useRouter();

// Fetch product details
const fetchDetailProduct = async () => {
    try {

        //fetch data product berdasarkan id
        const response = await Api.get(`/api/products/${route.params.id}`);

        //set data ke state
        title.value = response.data.data.title;
        description.value = response.data.data.description;
        price.value = response.data.data.price;
        stock.value = response.data.data.stock;

    } catch (error) {

        //log error
        console.error("Error fetching product:", error);
    }
};

//run hook "onMounted"
onMounted(() => {

    //call method "fetchDetailProduct"
    fetchDetailProduct();
});

// Handle file change
const handleFileChange = (event: Event) => {
    const target = event.target as HTMLInputElement;
    if (target.files && target.files[0]) {
        image.value = target.files[0];
    }
};

// Handle form submission
const updateProduct = async () => {

    //initiate form data
    const formData = new FormData();

    //append data to form data
    if (image.value) formData.append("image", image.value);
    formData.append("title", title.value);
    formData.append("description", description.value);
    formData.append("price", price.value);
    formData.append("stock", stock.value);
    formData.append("_method", "PUT");

    try {

        //send data to api
        await Api.post(`/api/products/${route.params.id}`, formData);

        //redirect to products page
        router.push("/products");

    } catch (error: any) {

        //set error to state
        errors.value = error.response.data;
    }
};
</script>

<template>
    <div class="container mt-5">
        <div class="row">
            <div class="col-md-12">
                <div class="card border-0 rounded-3 shadow">
                    <div class="card-body">
                        <form @submit.prevent="updateProduct">
                            <div class="mb-3">
                                <label class="form-label fw-bold">Image</label>
                                <input type="file" @change="handleFileChange" class="form-control" />
                                <div v-if="errors.image" class="alert alert-danger mt-2">
                                    {{ errors.image[0] }}
                                </div>
                            </div>
                            <div class="mb-3">
                                <label class="form-label fw-bold">Title</label>
                                <input type="text" v-model="title" class="form-control" placeholder="Title Product" />
                                <div v-if="errors.title" class="alert alert-danger mt-2">
                                    {{ errors.title[0] }}
                                </div>
                            </div>
                            <div class="mb-3">
                                <label class="form-label fw-bold">Description</label>
                                <textarea v-model="description" class="form-control" rows="5"
                                    placeholder="Description Product"></textarea>
                                <div v-if="errors.description" class="alert alert-danger mt-2">
                                    {{ errors.description[0] }}
                                </div>
                            </div>
                            <div class="row">
                                <div class="col-md-6">
                                    <div class="mb-3">
                                        <label class="form-label fw-bold">Price</label>
                                        <input type="number" v-model="price" class="form-control"
                                            placeholder="Price Product" />
                                        <div v-if="errors.price" class="alert alert-danger mt-2">
                                            {{ errors.price[0] }}
                                        </div>
                                    </div>
                                </div>
                                <div class="col-md-6">
                                    <div class="mb-3">
                                        <label class="form-label fw-bold">Stock</label>
                                        <input type="number" v-model="stock" class="form-control"
                                            placeholder="Stock Product" />
                                        <div v-if="errors.stock" class="alert alert-danger mt-2">
                                            {{ errors.stock[0] }}
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <button type="submit" class="btn btn-md btn-primary rounded-5 shadow border-0">Update</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

Dari penambahan kode di atas, pertama kita inisialisasi component menggunakan TypeScript dengan cara menambahkan attribute lang="ts" pada script setup.

<script setup lang="ts">

	//...
	
</script>

Di dalamnya, kita import ref dan onMounted dari Vue.

//import useRoute dan useRouter dari vue-router
import { useRoute, useRouter } from "vue-router";

Kemudian kita import juga service API.

//import Api dari folder api
import Api from "../../api";

Selanjutnya kita buat interface baru dengan nama Errors. Interface ini akan kita gunakan untuk mendefinisikan object dari error response yang didapatkan dari Rest API.

// Interface Errors
interface Errors {
    image?: string[];
    title?: string[];
    description?: string[];
    price?: string[];
    stock?: string[];
}

Dan kita buat beberapa state untuk menampung data dari form.

// State untuk form
const image = ref<File | null>(null);
const title = ref("");
const description = ref("");
const price = ref("");
const stock = ref("");

// State errors
const errors = ref<Errors>({});

Selanjutnya, kita inisialisasi route dan router dari Vue Router.

//initiate route and router
const route = useRoute();
const router = useRouter();
  • route - akan kita gunakan untuk mendapatkan id dari parameter URL di browser.
  • router - akan kita gunakan untuk navigasi atau berpindah halaman.

Setelah itu, kita buat function baru dengan nama fetchDetailPost.

// Fetch product details
const fetchDetailProduct = async () => {

	//...
	
}

Di dalamnya kita melakukan fetching ke Rest API berdasarkan parameter id yang didapatkan dari URL.

//fetch data product berdasarkan id
const response = await Api.get(`/api/products/${route.params.id}`);

Jika data ditemukan, maka kita akan assign response data-nya ke dalam state.

//set data ke state
title.value = response.data.data.title;
description.value = response.data.data.description;
price.value = response.data.data.price;
stock.value = response.data.data.stock;

Agar function fetchDetailPost bisa jalankan saat halaman diakses, maka kita perlu memanggilnya di dalam hook onMounted.

//run hook "onMounted"
onMounted(() => {

    //call method "fetchDetailProduct"
    fetchDetailProduct();
});

Kita buat function lagi untuk menghandle file yang akan diupload.

// Handle file change
const handleFileChange = (event: Event) => {
    const target = event.target as HTMLInputElement;
    if (target.files && target.files[0]) {
        image.value = target.files[0];
    }
};

Terakhir, kita buat function yang bernama updateProduct. Function ini akan dijalakan ketika form disumbit.

 <form @submit.prevent="updateProduct">
 
 	//...
 	
 </form>
// Handle form submission
const updateProduct = async () => {

	//...
	
}

Di dalam function di atas, sama seperti yang ada pada function storePost pada pembahasan artikel sebelumnya, bedanya disini kita tambahkan sebuah key _method dengan value PUT pada append. Karena data yang dikirimkan adalah proses update data.

//append data to form data
if (image.value) formData.append("image", image.value);
formData.append("title", title.value);
formData.append("description", description.value);
formData.append("price", price.value);
formData.append("stock", stock.value);
formData.append("_method", "PUT");

Setelah itu, kita tinggal kirimkan data-nya melalui Axios dengan method POST.

//send data to api
await Api.post(`/api/products/${route.params.id}`, formData);

Jika proses update data berhasil dilakukan, maka kita redirect atau arahkan pada halaman products index.

//redirect to products page
router.push("/products");

Jika data gagal diupdate, maka kita assign error response validasinya ke dalam state errors.

//set error to state
errors.value = error.response.data;

Langkah 2 - Konfigurasi Route Products Edit

Silahkan teman-teman buka file src/routes/index.ts, kemudian ubah kode-nya menjadi seperti berikut ini.

src/routes/index.ts

//import createRouter, createWebHistory and Type RouteRecordRaw from vue-router
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'

// Define route type with explicit type annotations
const routes: Array<RouteRecordRaw> = [
    {
        path: '/',
        name: 'home',
        component: () => import(/* webpackChunkName: "home" */ '../views/home.vue')
    },
    {
        path: '/products',
        name: 'products',
        component: () => import(/* webpackChunkName: "products" */ '../views/products/index.vue')
    },
    {
        path: '/products/create',
        name: 'products-create',
        component: () => import(/* webpackChunkName: "products-create" */ '../views/products/create.vue')
    },
    {
        path: '/products/edit/:id',
        name: 'products-edit',
        component: () => import(/* webpackChunkName: "products-edit" */ '../views/products/edit.vue')
    },
]

// Create router with explicit type annotations
const router = createRouter({
    history: createWebHistory(),
    routes
})

export default router

Di atas, kita membuat konfigurasi route baru, dimana untuk path atau URL-nya kita arahkan ke /products/edit/:id dan component atau view yang dipanggil adalah views/products/edit.vue.

Langkah 3 - Uji Coba Edit dan Update Data

Silahkan teman-teman klik button EDIT yang ada pada data yang dimiliki, jika berhasil maka akan menampilkan halaman form edit data seperti berikut.

Silahkan sesuaikan isinya, kemudian klik button Update. Jika berhasil maka kita akan diarahkan ke halaman products index dengan data yang telah diperbarui.

Kesimpulan

Pada artikel ini, kita telah belajar bagaimana cara menampilkan detail data product yang akan diedit pada sebuah form, kemudian melakukan proses update di Vue 3 TypeScript.

Pada artikel selanjutnya, kita semua akan belajar bagaimana cara membuat fitur proses delete data product dengan Rest API di Vue 3 TypeScript.

Terima Kasih


Fika Ridaul Maulayya
Full-Stack Developer, Content Creator and CO-Founder SantriKoding.com

Suka dengan tulisan di SantriKoding? Kamu bisa memberikan dukungan dengan berdonasi atau bagikan konten ini di sosial media. Terima kasih atas dukungan Anda!

KEBIJAKAN KOMENTAR

Saat memberikan komenatar silahkan memberikan informasi lengkap tentang error, seperti: screenshot, link kode, dll. Baca aturan komentar kami