In this article, we are going to implement a sample Vue(3.0) CRUD example, using JSON Server(Fake API).
Vue(3.0):
Vue(3.0) is a javascript framework for creating a single-page application. Vue application built by component. The components are the smallest unit of the application which comprises 'Script', 'Template(HTML)', and 'Style'. Eventually, multiple components together create the Vue application.
Create Vue(3.0) Application:
Let's create a sample Vue(3.0) application to accomplish our demo.
To create a VueJS application our local machine should contain NodeJS. So go to "https://nodejs.org/en/download" and download the Node.
Now run the below command to create the Vue3 application.
npm init vue@latest
On running the above command we have to choose a few options before creating a vue application, in those options we can choose the default option as 'No', but for the routing option select 'Yes' like below.
Now open our vue application in 'Visual Studio Code' editor and then run the 'npm install' command to download the required packages.
Let's explore the project default files & folders structure:
package.json - the 'package.json' file contains package references, commands, etc.
index.html - the 'index.html' file is the only HTML file of our vue application. It contains a 'div' element with 'id' value 'app', so inside of those elements our components get rendered.
main.js - entry js file.
App.vue - entry vue component.
views(folder) - contains vue component that acts as an individual page.
router(folder) - contains js file which contains route configuration.
components(folder) - contains vue components that can be used as children's components
assets(folder) - contains static files like images.
Run the below command to start the VueJS application under the local server.
npm run dev
Setup JSON Server(Fake API):
Let's set up a fake API by setting up the JSON server in our local machine.
Run the below command to install the JSON server globally onto your local system.
npm install -g json-server
Now go to our Vue application and add a command to run the JSON server into the 'pakage.json' file
Now to invoke the above added command, run the below command in vue application root folder
npm run json-run
After running the above command for the first time, a 'db.json' file gets created, so this file act as a database. So let's add some sample data to the file as below.
Now access the fake JSON endpoint like 'http://localhost:3000/wonders'
Configure Bootstrap Menu:
In the 'index.html' add the bootstrap CSS and JS file references.
index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite App</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous" /> </head> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous" ></script> </body> </html>
- (Line: 8-13) Bootstrap CSS reference
- (Line: 18-22) Bootstrap JS reference
Now let's update the 'App.vue' component with the bootstrap menu as follows.
src/App.vue:
<script setup> import { RouterLink, RouterView } from "vue-router"; </script> <template> <nav class="navbar bg-primary" data-bs-theme="dark"> <div class="container-fluid"> <a class="navbar-brand" href="#">7 Wonders Of The World</a> </div> </nav> <div class="container"> <RouterView /> </div> </template> <style scoped> </style>
- Here added our bootstrap. The 'RouterView' is the default component loads from the 'vue-router'. The 'RouterView' component renders the page content
Install Axio Library:
To consume API calls we need to install Axios library
npm install axios
Create A 'AllWonders' Vue Component:
Let's create a new vue component 'AllWonders.vue' in 'src/views/wonders'(new folder).
Now configure the route for 'AllWonders.vue' component in the 'router/index.js'.
src/router/index.js:
import { createRouter, createWebHistory } from "vue-router"; import AllWonders from "../views/wonders/AllWonders.vue"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: "/", name: "home", component: AllWonders, }, ], }); export default router;
- Here configured a route like '/' to our 'AllWonders' component.
Implement Read Operation:
Read operation means invoking the HTTP GET endpoint, and rendering the API response to the UI.
src/views/wonders/AllWonders.vue:
<script setup> import { ref, onMounted } from "vue"; import axios from "axios"; const allWonders = ref([]); onMounted(() => { axios.get("http://localhost:3000/wonders").then((response) => { allWonders.value = response.data; }); }); </script> <template> <div class="container mt-3"> <div class="row row-cols-1 row-cols-md-3 g-4"> <div class="col" v-for="item in allWonders" :key="item.id"> <div class="card"> <img :src="item.imageUrl" class="card-img-top" alt="..." /> <div class="card-body"> <h5 class="card-title">{{ item.name }}</h5> <p class="card-text">Location: {{ item.location }}</p> </div> </div> </div> </div> </div> </template>
- (Line: 1) The 'setup' attribute added to the script tag that represents we are using the composition API approach.
- (Line: 4) Declared 'allWonders' variable of type 'ref'. The 'ref' type is used to declare the reactive properties. Here 'ref([])' means an empty array will be the initial value for the 'allWonders' variable. The 'ref' imports from the 'vue' library.
- (Line: 5) The 'onMounted' is the life cycle method. Here we can add our logic that needs to be executed on the component mounted. Here generally we invoke our APIS whose response we want to display on page load.
- (Line: 6-8) Using 'axios.get()' invoking our HTTP GET API endpoint. Here API response assigned to 'allWonders' variable. In vue to assign value to 'ref' variable we should assign to '{variable}.value', we can't directly assign it to the variable itself.
- (Line: 12-24) Inside the 'template' element we have to render all our component HTML content.
- (Line: 14) The 'v-for' attribute is used to loop the HTML element based on the collection variable(allWonders). The ':key' attribute is assigned with a unique values like 'id' this helps with HTML track.
- In vue to render any data dynamically or data binding we have to use ''{{}}"
Create A 'AddWonders.vue' Component:
Let's create a new Vue component 'AddWornders.vue' in 'src/views/wonders'.
Now configure routing for 'AddWonders.vue' component in the 'router/index.js'.
src/router/index.js:
import { createRouter, createWebHistory } from "vue-router"; import AllWonders from "../views/wonders/AllWonders.vue"; import AddWonders from "../views/wonders/AddWonders.vue"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: "/", name: "home", component: AllWonders, }, { path: "/add-wonder", name: "Add Wonder", component: AddWonders, }, ], }); export default router;
- (Line: 12-16) Configured the '/add-wonder' router for 'AddWonders.vue' component.
Implement Create Operation:
The Create operation means posting the data to the HTTP POST API call for creating the new item.
src/views/wonders/AddWonders.vue:
<script setup> import axios from "axios"; import { reactive } from "vue"; import { useRouter } from "vue-router"; let newWonder = reactive({ name: "", location: "", imageUrl: "", }); const router = useRouter(); const addNewWonder = () => { axios.post("http://localhost:3000/wonders", newWonder).then(() => { router.push("/"); }); }; </script> <template> <div class="container mt-4 w-50"> <form @submit.prevent="addNewWonder"> <legend >Add New Wonder's</legend> <div class="mb-3"> <label for="txtwonderName" class="form-label">Wonder Name</label> <input type="text" v-model="newWonder.name" class="form-control" id="txtwonderName" /> </div> <div class="mb-3"> <label for="txtLocation" class="form-label">Location</label> <input type="text" v-model="newWonder.location" class="form-control" id="txtLocation" /> </div> <div class="mb-3"> <label for="txtImageUrl" class="form-label">Image URL</label> <input type="text" v-model="newWonder.imageUrl" class="form-control" id="txtImageUrl" /> </div> <button type="submit" class="btn btn-primary">Add</button> </form> </div> </template>
- (Line: 1) The 'setup' attribute added to the script tag that represents we are using the composition API approach.
- (Line: 5-9) The 'reactive' type loads from 'vue' library. The 'reactive' type is appropriate for creating models for the form binding. Here we defined properties like 'name', 'location', and 'imageUrl' which can use for form binding.
- (Line: 11-15) The 'addNewWonder' method contains logic to invoke the HTTP Post API call.
- In vue to enable the form model binding we will use 'v-model' attribute. Each form field must be decorated with the 'v-model'.
- (Line: 20) The '@submit.prevent' event raises on clicking the form submit button, but it won't reload the page since we configured the 'prevent' event. Here '@submit.prevent' is registered with 'addNewWonder' method.
src/views/wonders/Allwonders.vue:
<script setup> import { ref, onMounted } from "vue"; import axios from "axios"; const allWonders = ref([]); onMounted(() => { axios.get("http://localhost:3000/wonders").then((response) => { allWonders.value = response.data; }); }); </script> <template> <div class="container mt-3"> <div class="row m-2"> <div class="col col-md-4 offset-md-4"> <RouterLink to="/add-wonder" class="btn btn-primary">Add</RouterLink> </div> </div> <div class="row row-cols-1 row-cols-md-3 g-4"> <div class="col" v-for="item in allWonders" :key="item.id"> <div class="card"> <img :src="item.imageUrl" class="card-img-top" alt="..." /> <div class="card-body"> <h5 class="card-title">{{ item.name }}</h5> <p class="card-text">Location: {{ item.location }}</p> </div> </div> </div> </div> </div> </template>
- (Line: 15) Added the 'RouterLink' its 'to' attribute configured with '/add-wonder' route.
(Step 2)
(Step 3)
Create A 'EditWonders.vue' Component:
Let's create a new Vue component 'EditWornders.vue' in 'src/views/wonders'.
Now configure routing for the 'EditWonders.vue' component in 'router/index.js'.src/router/index.js:
import { createRouter, createWebHistory } from "vue-router"; import AllWonders from "../views/wonders/AllWonders.vue"; import AddWonders from "../views/wonders/AddWonders.vue"; import EditWonders from "../views/wonders/EditWonders.vue"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: "/", name: "home", component: AllWonders, }, { path: "/add-wonder", name: "Add Wonder", component: AddWonders, }, { path: "/edit-wonder/:id", name: "Edit Wonder", component: EditWonders, } ], }); export default router;
- (Line: 18-22) configured the 'edit-wonder/:id' route for the 'EditWonders.vue' component. Here ':id' is the dynamic placeholder where we define an item to edit the 'id' value in the route.
Implement Update Operation:
The update operation means invoking the HTTP PUT endpoint to update the existing item.
src/views/wonders/EditWonders.vue:
<script setup> import axios from "axios"; import { reactive, onMounted } from "vue"; import { useRouter, useRoute } from "vue-router"; let wonderToUpdate = reactive({ id: 0, name: "", location: "", imageUrl: "", }); const router = useRouter(); const route = useRoute(); onMounted(() => { axios .get(`http://localhost:3000/wonders/${route.params.id}`) .then((response) => { wonderToUpdate.id = response.data.id; wonderToUpdate.name = response.data.name; wonderToUpdate.location = response.data.location; wonderToUpdate.imageUrl = response.data.imageUrl; }); }); const updateWonder = () => { axios.put(`http://localhost:3000/wonders/${route.params.id}`, wonderToUpdate).then(() => { router.push("/"); }); }; </script> <template> <div class="container mt-4 w-50"> <form @submit.prevent="updateWonder"> <legend>Update Wonders</legend> <div class="mb-3"> <label for="txtWonderName" class="form-label">Name</label> <input type="text" v-model="wonderToUpdate.name" class="form-control" id="txtWonderName" /> </div> <div class="mb-3"> <label for="txtLocation" class="form-label">Location</label> <input type="text" v-model="wonderToUpdate.location" class="form-control" id="txtLocation" /> </div> <div class="mb-3"> <label for="txtImageUrl" class="form-label">Image URL</label> <input type="text" v-model="wonderToUpdate.imageUrl" class="form-control" id="txtImageUrl" /> </div> <button type="submit" class="btn btn-primary">Update</button> </form> </div> </template>
- (Line: 1) The 'setup' attribute added to the script tag that represents we are using the composition API approach.
- (Line: 6-11) The 'reactive' type loads from the 'vue' library. The 'reactive' type is appropriate for creating the model for the form binding in vue. Here we defined properties like 'id', 'name', 'location', and 'imageUrl' which can use for the form binding.
- (Line: 13) The 'useRouter' loads from the 'vue-router' library that helps with navigation.
- (Line: 14) The 'useRoute' loads from the 'vue-router' library that helps to read the URL parameters.
- (Line; 16-25) The 'onMounted' is a Vue life-cycle method in which we invoke the get by 'id' endpoint to populate the item data on to our update form.
- (Line: 27-31) Here we defined a method like 'updateWonder' that contains logic to invoke the HTTP PUT request to update the item.
- In vue enable the form model binding we will use 'v-model' attribute. Each form field must be decorate with 'v-model'.
- (Line: 35) The '@submit.prevent' event raises on clicking the form submit button, but it won't reload the page since we configured the 'prevent' event. Here '@submit.prevent' is registered with 'updateWonder' method.
src/views/wonders/EditWonders.vue:
<div class="card"> <img :src="item.imageUrl" class="card-img-top" alt="..." /> <div class="card-body"> <h5 class="card-title">{{ item.name }}</h5> <p class="card-text">Location: {{ item.location }}</p> <router-link class="btn btn-primary" :to="`/edit-wonder/${item.id}`">Edit</router-link> </div> </div>
- (Line: 6) The 'Routerlink' is configured as our Edit button. Here is our dynamically generated edit route adding at 'to' attribute.
(Step 2)
(Step 3)
Create A 'ConfirmDeletePopup' Component:
Let's create 'ConfirmDeletePopup.vue' VueJS component in 'src/components' folder, this component can be used as global and its functionality is to show a popup for deleting any kind of item for your application.
src/components/ConfirmDeletePopup.vue:
<script setup></script> <template> <div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" > <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="exampleModalLabel"> Delete Confirmation! </h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" ></button> </div> <div class="modal-body">Are you sure to delete this item</div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" > Close </button> <button type="button" @click="$emit('confirmdelete-click')" class="btn btn-danger" > Confirm Delete </button> </div> </div> </div> </div> </template>
- Here we added the bootstrap modal HTML content.
- (Line: 5) Make sure to give 'id' attribute value fo the bootstrap modal.
- (Line: 34) Here button click emits 'confirmdelete-click' which means we are trying to receive an event from the child component(ConfirmDeletePopup.vue) to the parent component(like 'AllWonders.vue'). To emit the event we have to use '$emit()'.
Implement Delete Operation:
The delete operation means invoking the HTTP Delete endpoint for deleting the item.
src/views/wonders/AllWonders.vue:
<script setup> import { ref, onMounted } from "vue"; import ConfirmDeletePopup from "../../components/ConfirmDeletePopup.vue"; import axios from "axios"; const allWonders = ref([]); const itemToDeleteId = ref([0]); let deleteModal; onMounted(() => { deleteModal = new window.bootstrap.Modal( document.getElementById("deleteModal")); axios.get("http://localhost:3000/wonders").then((response) => { allWonders.value = response.data; }); }); const openDeleteModal = (id) => { itemToDeleteId.value = id; deleteModal.show(); }; const confirmDelete = () => { axios .delete(`http://localhost:3000/wonders/${itemToDeleteId.value}`) .then(() => { allWonders.value = allWonders.value.filter( (_) => _.id !== itemToDeleteId.value ); itemToDeleteId.value = 0; deleteModal.hide(); }); }; </script> <template> <div class="container mt-3"> <div class="row m-2"> <div class="col col-md-4 offset-md-4"> <RouterLink to="/add-wonder" class="btn btn-primary">Add</RouterLink> </div> </div> <div class="row row-cols-1 row-cols-md-3 g-4"> <div class="col" v-for="item in allWonders" :key="item.id"> <div class="card"> <img :src="item.imageUrl" class="card-img-top" alt="..." /> <div class="card-body"> <h5 class="card-title">{{ item.name }}</h5> <p class="card-text">Location: {{ item.location }}</p> <router-link class="btn btn-primary" :to="`/edit-wonder/${item.id}`" >Edit</router-link > | <button type="button" @click="openDeleteModal(item.id)" class="btn btn-danger" > Delete </button> </div> </div> </div> </div> </div> <ConfirmDeletePopup @confirmdelete-click="confirmDelete"></ConfirmDeletePopup> </template>
- (Line: 7) Declared the variable like 'deleteModal'.
- (Line: 10-11) Bootstrap instance assigned to the 'deleteModal'.Here we use the modal 'id' attribute value while creating the bootstrap instance.
- (Line: 18-21) The 'openDeleteModal' method contains logic to show the delete confirmation modal on clicking the delete button. Here we set the item to delete 'id' value to 'itemToDelete' variable.
- (Line: 23-33) The 'confirmDelete' method contains logic to invoke the delete
- API call. On successful deletion, we are going to update our 'allWonders' variable and then hide the bootstrap modal.
- (Line: 52-58) Here we added the 'Delete' button and its click registered with the 'openDeleteModal' method.
- (Line: 64)Rendered our 'ConfirmDeletePopup' component and registered with '@confirmdelete-click' which reads the button click from the child component(ConfirmDeletePopup.vue)
Support Me!
Buy Me A Coffee
PayPal Me
Video Session:
Wrapping Up:
Hopefully, I think this article delivered some useful information on the Vue(3.0) CRUD sample. using I love to have your feedback, suggestions, and better techniques in the comment section below.
Comments
Post a Comment