Vue Typescript Example: Build A CRUD Application - BezKoder
Vue Typescript Example: Build A CRUD Application - BezKoder
More Practice:
– Vue/Vuex Typescript: JWT Authentication example
– Vue File Upload example using Axios
Fullstack:
– Vue.js + Node.js + Express + MySQL example
– Vue.js + Node.js + Express + PostgreSQL example
– Vue.js + Node.js + Express + MongoDB example
– Vue.js + Spring Boot + MySQL/PostgreSQL example
– Vue.js + Spring Boot + MongoDB example
– Vue.js + Django example
– Create an object:
– Retrieve all objects:
– Click on Edit button to update an object:
On this Page, you can:
You can find step by step to build a Server like this in one of
these posts:
– Express, Sequelize & MySQL
– Express, Sequelize & PostgreSQL
– Express & MongoDb
– Spring Boot & MySQL
– Spring Boot & PostgreSQL
– Spring Boot & MongoDB
– Spring Boot & Cassandra
– Django & MySQL
– Django & PostgreSQL
– Django & MongoDB
All of them can work well with this Vue Typescript App.
Technology
vue: 2.6.11
vue-router: 3.1.6
axios: 0.19.2
vue-class-component 7.2.3
vue-property-decorator 8.4.1
Project Structure
Let me explain it briefly.
public
index.html
src
components
AddTutorial.vue
Tutorial.vue
TutorialsList.vue
services
TutorialDataService.ts
App.vue
main.ts
package.json
Open public/index.html, add bootstrap inside <head> tag:
<!DOCTYPE html>
<html lang="en">
<head>
...
<title>Vue Typescript CRUD</title>
<link type="text/css" rel="stylesheet" href="//unpkg.com/boots
</head>
<body>
...
</body>
</html>
Vue.use(VueRouter);
new Vue({
router,
render: h => h(App)
}).$mount("#app");
<template>
<div id="app">
<nav class="navbar navbar-expand navbar-dark bg-dark">
<a href="#" class="navbar-brand">bezKoder</a>
<div class="navbar-nav mr-auto">
<li class="nav-item">
<a href="/tutorials" class="nav-link">Tutorials</a>
</li>
<li class="nav-item">
<a href="/add" class="nav-link">Add</a>
</li>
</div>
</nav>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component
export default class App extends Vue {}
</script>
services/TutorialDataService.ts
class TutorialDataService {
getAll() {
return http.get("/tutorials");
}
get(id: string) {
return http.get(`/tutorials/${id}`);
}
create(data: any) {
return http.post("/tutorials", data);
}
delete(id: string) {
return http.delete(`/tutorials/${id}`);
}
deleteAll() {
return http.delete(`/tutorials`);
}
findByTitle(title: string) {
return http.get(`/tutorials?title=${title}`);
}
}
components/AddTutorial.vue
<template>
<div class="submit-form">
<div v-if="!submitted">
<div class="form-group">
<label for="title">Title</label>
<input
type="text"
class="form-control"
id="title"
required
v-model="tutorial.title"
name="title"
/>
</div>
<div class="form-group">
<label for="description">Description</label>
<input
class="form-control"
id="description"
required
v-model="tutorial.description"
name="description"
/>
</div>
<div v-else>
<h4>You submitted successfully!</h4>
<button class="btn btn-success" @click="newTutorial">Add
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import TutorialDataService from "../services/TutorialDataService"
@Component
export default class AddTutorial extends Vue {
private tutorial: any = {
id: null,
title: "",
description: "",
published: false,
};
saveTutorial() {
var data = {
title: this.tutorial.title,
description: this.tutorial.description,
};
TutorialDataService.create(data)
.then((response) => {
this.tutorial.id = response.data.id;
console.log(response.data);
this.submitted = true;
})
.catch((e) => {
console.log(e);
});
}
newTutorial() {
this.submitted = false;
this.tutorial = {};
}
}
</script>
<style scoped>
.submit-form {
max-width: 300px;
margin: auto;
}
</style>
getAll()
deleteAll()
findByTitle()
components/TutorialsList.vue
<template>
<div class="list row">
<div class="col-md-8">
<div class="input-group mb-3">
<input
type="text"
class="form-control"
placeholder="Search by title"
v-model="title"
/>
<div class="input-group-append">
<button
class="btn btn-outline-secondary"
type="button"
@click="searchTitle"
>
Search
</button>
</div>
</div>
</div>
<div class="col-md-6">
<h4>Tutorials List</h4>
<ul class="list-group">
<li
class="list-group-item"
:class="{ active: index == currentIndex }"
v-for="(tutorial, index) in tutorials"
:key="index"
@click="setActiveTutorial(tutorial, index)"
>
{{ tutorial.title }}
</li>
</ul>
<a
class="badge badge-warning"
:href="'/tutorials/' + currentTutorial.id"
>
Edit
</a>
</div>
<div v-else>
<br />
<p>Please click on a Tutorial...</p>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import TutorialDataService from "../services/TutorialDataService"
@Component
export default class TutorialsList extends Vue {
private tutorials: any[] = [];
private currentTutorial: any = null;
private currentIndex: number = -1;
private title: string = "";
retrieveTutorials() {
TutorialDataService.getAll()
.then((response) => {
this.tutorials = response.data;
console.log(response.data);
})
.catch((e) => {
console.log(e);
});
}
refreshList() {
this.retrieveTutorials();
this.currentTutorial = null;
this.currentIndex = -1;
}
removeAllTutorials() {
TutorialDataService.deleteAll()
.then((response) => {
console.log(response.data);
this.refreshList();
})
.catch((e) => {
console.log(e);
});
}
searchTitle() {
TutorialDataService.findByTitle(this.title)
.then((response) => {
this.tutorials = response.data;
console.log(response.data);
})
.catch((e) => {
console.log(e);
});
}
mounted() {
this.retrieveTutorials();
}
}
</script>
<style scoped>
.list {
text-align: left;
max-width: 750px;
margin: auto;
}
</style>
If you click on Edit button of any Tutorial, the app will direct
you to Tutorial page with url: /tutorials/:tutorialId.
get()
update()
delete()
components/Tutorial.vue
<template>
<div v-if="currentTutorial" class="edit-form">
<h4>Tutorial</h4>
<form>
<div class="form-group">
<label for="title">Title</label>
<input
type="text"
class="form-control"
id="title"
v-model="currentTutorial.title"
/>
</div>
<div class="form-group">
<label for="description">Description</label>
<input
type="text"
class="form-control"
id="description"
v-model="currentTutorial.description"
/>
</div>
<div class="form-group">
<label><strong>Status:</strong></label>
{{ currentTutorial.published ? "Published" : "Pending" }}
</div>
</form>
<button
class="badge badge-primary mr-2"
v-if="currentTutorial.published"
@click="updatePublished(false)"
>
UnPublish
</button>
<button
v-else
class="badge badge-primary mr-2"
@click="updatePublished(true)"
>
Publish
</button>
<div v-else>
<br />
<p>Please click on a Tutorial...</p>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import TutorialDataService from "../services/TutorialDataService"
@Component
export default class Tutorial extends Vue {
private currentTutorial: any = null;
private message: string = "";
getTutorial(id: string) {
TutorialDataService.get(id)
.then((response) => {
this.currentTutorial = response.data;
console.log(response.data);
})
.catch((e) => {
console.log(e);
});
}
updatePublished(status: boolean) {
var data = {
id: this.currentTutorial.id,
title: this.currentTutorial.title,
description: this.currentTutorial.description,
published: status,
};
TutorialDataService.update(this.currentTutorial.id, data)
.then((response) => {
this.currentTutorial.published = status;
console.log(response.data);
})
.catch((e) => {
console.log(e);
});
}
updateTutorial() {
TutorialDataService.update(this.currentTutorial.id, this.curre
.then((response) => {
console.log(response.data);
this.message = "The tutorial was updated successfully!"
})
.catch((e) => {
console.log(e);
});
}
deleteTutorial() {
TutorialDataService.delete(this.currentTutorial.id)
.then((response) => {
console.log(response.data);
this.$router.push({ name: "tutorials" });
})
.catch((e) => {
console.log(e);
});
}
mounted() {
this.message = "";
this.getTutorial(this.$route.params.id);
}
}
</script>
<style scoped>
.edit-form {
max-width: 300px;
margin: auto;
}
</style>
module.exports = {
devServer: {
port: 8081
}
}
This Vue Client will work well with following back-end Rest
APIs:
– Express, Sequelize & MySQL
– Express, Sequelize & PostgreSQL
– Express & MongoDb
– Spring Boot & MySQL
– Spring Boot & PostgreSQL
– Spring Boot & MongoDB
– Spring Boot & Cassandra
– Django & MySQL
– Django & PostgreSQL
– Django & MongoDB
Conclusion
Today we’ve built a Vue Typescript example – CRUD
Application successfully with Vue Router, Axios, Bootstrap.
Now we can consume REST APIs, display and modify data in
a clean way. I hope you apply it in your project at ease.
Source Code
You can find the complete source code for this tutorial on
Github.