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