Tutorial Vue 3 TypeScript #4: Insert Data Dengan Rest API


Tutorial Vue 3 TypeScript #4: Insert Data Dengan Rest API

Halo teman-teman semuanya, pada artikel sebelumnya kita smeua telah belajar bagaimana cara menampilkan data di Vue 3 TypeScript dari Rest API dan pada artikel kali ini kita semua akan belajar bagaimana cara melakukan proses insert dan upload gambar di Vue 3 TypeScript dengan Rest API.

Langkah 1 - Insert dan Upload Data di Vue 3

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

src/views/products/create.vue

<script setup lang="ts">

//import ref dari vue
import { ref } from "vue";

//import useRouter dari vue-router
import { 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 untuk error
const errors = ref<Errors>({});

// Router instance
const router = useRouter();

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

// Handle submit form
const storeProduct = async () => {

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

    //append data ke 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);

    try {

        //send data ke api
        await Api.post("/api/products", formData);

        //redirect ke halaman products
        router.push("/products");

    } catch (error: any) {

        //set error ke 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="storeProduct">
                            <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">Save</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 dari Vue.

//import ref dari vue
import { ref } from "vue";

Kemudian kita import hook useRouter dari Vue Router.

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

Selanjutnya, kita import service API.

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

Dan 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[];
}

Kemudian kita membuat beberapa state yang nanti digunakan 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 untuk error
const errors = ref<Errors>({});

Di atas untuk state price dan stock, kita menggunakan tipe string karena FormData hanya mendukung string. Nantinya, sebelum dikirim ke API, nilai dari kedua state ini akan tetap dalam bentuk string agar kompatibel dengan FormData.

Dan untuk state errors kita gunakan interface yang kita buat sebelumnya, yaitu Errors.

Setelah itu, kita inisialisasi router dari hook useRouter. Ini untuk mempermudah kita dalam menggunakannya nanti.

// Router instance
const router = useRouter();

Kemudian kita buat function baru dengan nama handleFileChange. Function ini yang digunakan untuk memasukkan file yang diambil dari komputer ke dalam state image.

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

Function handleFileChange di atas akan dijalankan, ketika input file gambar di dalam form dipilih.

<input type="file" @change="handleFileChange" class="form-control" />

Setelah itu, kita buat function lagi dengan nama storeProduct. Method ini akan dijalankan ketika form disubmit.

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

	//...
	
}

Di dalam method storePost di atas, pertama kita inisialisasi FormData. Karena kita akan melakukan upload gambar, maka kita harus menggunakan FormData agar file bisa dikirim ke server.

//init FormData
const formData = new FormData();

Setelah berhasil di inisialisasi, langkah berikutnya adalah memasukkan data yang ada di dalam state ke dalam FormData, yaitu dengan menggunakan append.

//append data ke 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);

Setelah data berhasil dimasukkan ke dalam FormData, langkah berikutnya adalah melakukan kirim data menggunakan Rest API dengan bantuan Axios, yaitu menggunakan method POST.

//send data ke api
await Api.post("/api/products", formData);

Jika proses insert dan upload data berhasil dilakukan di dalam server, maka kita akan redirect ke dalam halaman products index.

//redirect ke halaman products
router.push("/products");

Tapi jika data gagal disimpan, maka kita akan memasukkan error response validasi ke dalam state yang bernama errors.

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

Dan untuk menampilkan error di dalam template, kita bisa menggunakan kode seperti berikut ini.

<div v-if="errors.image" class="alert alert-danger mt-2">{{ errors.image[0] }}</div>
<div v-if="errors.title" class="alert alert-danger mt-2">{{ errors.title[0] }}</div>

Dan seterusnya.

Langkah 2 - Konfigurasi Route Products Create

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')
    },
]

// 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/create dan component atau view yang dipanggil adalah views/products/create.vue.

Langkah 3 - Uji Coba Insert dan Upload di Vue 3

Silahkan teman-teman klik button ADD NEW PRODUCT yang ada pada halaman products index atau juga bisa melalui link berikut ini http://localhost:5173/products/create, jika berhasil maka akan menampilkan halaman seperti berikut.

Silahkan klik button Save tanpa mengisi data apapun di dalam form, maka kita akan mendapatkan error validasi yang dikirimkan oleh Laravel melaluio Rest API.

Sekarang, silahkan masukkan data yang diinginkan di dalam form dan teman-teman klik Save. Jika berhasil maka kita akan diarahakan ke halaman products index dengan data yang berhasil disimpan.

Kesimpulan

Pada artikel ini, kita semua telah belajar bagaimana membuat proses insert dan upload data di Vue 3 TypeScript menggunakan Rest API.

Pada artikel selanjutnya, kita semua akan belajar bagaimana cara melakukan proses edit dan update data di Vue 3 TypeScript menggunakan Rest API.

Terima Kaish


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