Frontend using VueJS and TypeScript
In this article, I will be developing a Model-View-Controller (MVC) application using VueJS Framework, which would consume the REST APIs created in Microservices.
If you want to understand more about MVC Architecture, go through this article.
By the end of this article, you would learn about how to:
- Develop a Frontend MVC Application using VueJS Framework.
- How to call and consume REST APIs of microservice, from application developed using VueJS Framework.
If you have no time to read this article completely, but want to try the code for yourself, GitHub location is provided here.
Prerequisites
There are some prerequisites that are required for creating the Frontend Application.
Familiarity with Technologies and Frameworks
It is assumed that you have prior knowledge or familiarity with JavaScript, TypeScript and HTML. Because, I will not be covering the basics of these in this article.
If you are not familiar, then it is advised to get the basic knowledge of these and then come back to this site.
Node.js v8+
Download the latest version of the Node.js from here. Click the downloaded .msi and complete the installation.
Embedded npm available in the Node.js package.
webpack for building Frontend.
IDE for code development
You can use any IDE of your choice, that supports TypeScript. I will be using the Visual Studio Code.
If you wish to use the Visual Studio Code, download the latest version from here. Click on the downloaded .exe and complete the installation.
What is Vue?
Vue is a JavaScript framework for building user interfaces. It builds on top of standard HTML, CSS and JavaScript, and provides a declarative and component-based programming model that helps you efficiently develop user interfaces, be it simple or complex.
Vue is a framework and ecosystem that covers most of the common features needed in frontend development. Vue is designed to be flexible and incrementally adoptable.
Create a Base VueJS Project
A build setup allows us to use Vue Single-File Components (SFCs). The official Vue build setup is based on Vite, a frontend build tool that is modern, lightweight and extremely fast.
To create a build-tool-enabled Vue project on your machine, run the following command in your command line:
npm init vue@latest
This command will install and execute create-vue, the official Vue project scaffolding tool. You will be presented with prompts for a number of optional features such as TypeScript and testing support:
✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
Scaffolding project in ./<your-project-name>...
Done.
If you are unsure about an option, simply choose No
by hitting enter for now.
Once the project is created, follow the instructions to install dependencies:
cd <your-project-name>
npm install
To change the default port of the vue server
By default, the vue project runs on http://localhost:8080. If you have any other applciation running on that port (like I have my microservice running), we can change the port.
In the root folder «your-project-name» folder, create a file .env
and type in the below code (any available port number):
PORT=3000
Running the server
Start the dev server:
cd <your-project-name>
npm run dev
You should now have your first Vue project running!
Application Instance
Every Vue application starts by creating a new application instance with the createApp
function, this can be found in the main.js
file.
Every app requires a root component that can contain other components as its children. If you are using Single-File Components, we typically import the root component from another file:
1import { createApp } from 'vue'
2import App from './App.vue'
3
4createApp(App).mount('#app')
Adding Bootstrap Module to VueJS Project
Bootstrap Module helps with some pre built CSS and JavaScript for styling and is used widely. In order to add the Bootstrap Module to our VueJS project, follow the steps given below:
From inside the Project folder, type in the command:
npm install --save jquery popper.js
The Bootstrap’s latest versions assets will be installed in the node_modules/bootstrap
folder.
You can inform the VueJS application where to look for Bootstrap, by adding the following line in the file src/main.js
:
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'
Enhancing the Vanilla Application to consume REST APIs
Let us start building the frontend components for our Todo Tracker Application, so that we can consume the REST APIs exposed by our microservice application, mentioned below:
Description | CRUD Operation | HTTP Method | REST API Endpoint |
---|---|---|---|
Create New Todo Task | CREATE | POST | /tasks |
Fetch All Todo Tasks | READ | GET | /tasks |
Fetch One Todo Task | READ | GET | /tasks/{id} |
Update One Specific Todo Task | UPDATE | PUT | /tasks |
Delete One Specific Todo Task | DELETE | DELETE | /tasks/{id} |
Display All items
Description | CRUD Operation | HTTP Method | REST API Endpoint |
---|---|---|---|
Fetch All Todo Tasks | READ | GET | /tasks |
- To create components in vue we can use the vue-generate-component. We first need to install the vue-generate-component, by using the following command:
npm install vue-generate-component
- To create new component, go to
/src
folder and type the following command:
vgc TodoApplication
CLI would create new folder /src/TodoApplication
and generate 5 new files:
- index.vue
- TodoApplication.js
- TodoApplication.html
- TodoApplication.scss
- TodoApplication.spec.js
- Update the
/src/App.vue
1<template>
2 <div style="text-align:center">
3 <h1>Todo Tracker Application</h1>
4 <TodoApplication/>
5 </div>
6</template>
7
8<script>
9import TodoApplication from './TodoApplication/index.vue'
10
11export default {
12 name: 'App',
13 components: {
14 TodoApplication
15 }
16}
17</script>
- Update the
/src/TodoApplication/TodoApplication.html
1<section class="todo-application">
2 <div class="container">
3 <div class="container">
4 <table class='table-striped' border='1' align="center">
5 <thead>
6 <tr>
7 <th>Title</th>
8 <th>Description</th>
9 <th>Due Date</th>
10 <th>Status</th>
11 <th>No. Of Comments</th>
12 </tr>
13 </thead>
14 <tbody>
15 <tr v-for='todoApp in todoApplication'>
16 <td> {{ todoApp.title }} </td>
17 <td> {{ todoApp.description }} </td>
18 <td> {{ formatDate(todoApp.dueDate) }} </td>
19 <td> {{ todoApp.status }} </td>
20 <td v-if="todoApp.todoTaskCommentsSet != null"> {{ todoApp.todoTaskCommentsSet.length }} </td>
21 </tr>
22 </tbody>
23 </table>
24 </div>
25 </div>
26</section>
Hardcode the values
- Update the
/src/TodoApplication/TodoApplication.js
- Hardcode the values for now
1export default {
2 name: 'todo-application',
3 components: {},
4 props: [],
5 data () {
6 return {
7 todoApplication : [
8 { systemTasksId: 1, title: 'Hardcoded from Vue', description: 'Hardcoded from Vue', dueDate: '2022-06-30',
9 status: 'Not Started', todoTaskCommentsSet:[] }
10 ]
11 }
12 },
13 computed: {
14
15 },
16 mounted () {
17
18 },
19
20 methods: {
21 formatDate(date) {
22 return moment(date, 'YYYY-MM-DD').format('DD-MMM-YYYY');
23 }
24 }
25}
When the browser refreshes, if your updates are fine, you should see page similar to the following.
At this stage, we are able to display the list of components that are hardcoded onto our Vue Component.
Connect REST API with the VueJS Project
Before we continue with connecting our VueJS Project to REST API, we need to add module required to make the REST API calls – axios
- Add
axios
module to the project by opening the command prompt, in the project folder and type:
npm add axios
- Create a
http-common.js
file to provide the http related information.
1import axios from "axios";
2export default axios.create({
3 baseURL: "http://localhost:8080/todo-app/tasks",
4 headers: {
5 "Content-type": "application/json"
6 }
7});
- Create Service Component - to make the REST API calls =
/src/TodoApplication/TodoDataService.js
1import http from "../http-common";
2
3class TodoDataService {
4 getAll() {
5 return http.get();
6 }
7}
8export default new TodoDataService();
- Update the
/src/TodoApplication/TodoApplication.js
1import moment from 'moment'
2import TodoDataService from './TodoDataService'
3
4export default {
5 name: 'todo-application',
6 components: {},
7 props: [],
8 data () {
9 return {
10 todoApplication:[]
11 }
12 },
13
14 computed: {},
15
16 mounted () {
17 this.retrieveAll();
18 },
19
20 methods: {
21 formatDate(date) {
22 return moment(date, 'YYYY-MM-DD').format('DD-MMM-YYYY');
23 },
24
25 retrieveAll() {
26 TodoDataService.getAll()
27 .then(response => (
28 console.log(JSON.stringify(response.data))
29 ))
30 }
31 }
32}
When we refresh the Browser, we get the below CORS Request Error.
Access to XMLHttpRequest at 'http://localhost:8080/todo-app/tasks' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The microservice application’s REST API is running on http://localhost:8080/
, and it is not allowing requests from other servers/domains - http://localhost:3000/
(in our case here).
This is called CORS (Cross-Origin Resource Sharing) policy, where in by default, servers block request coming from other servers or domains.
Click here to know more about how to enable CORS Policy and resolve this error.
After enabling CORS on Microservice Application, if we refresh the browser - http://localhost:3000/
, we should see the output from REST APIs microservice application, printed on the browser console (Click Developer Tools from settings or F12 on most browsers).
Display REST API results on VueJS Component
Let us move ahead and display the REST API output onto the Vue component, hence on the Vue App browser page.
- Update the
/src/TodoApplication/TodoApplication.js
1export default {
2 name: 'todo-application',
3 components: {},
4 props: [],
5 data () {
6 return {
7 todoApplication:[]
8 }
9 },
10
11 computed: {},
12
13 mounted () {
14 this.retrieveAll();
15 },
16
17 methods: {
18 formatDate(date) {
19 return moment(date, 'YYYY-MM-DD').format('DD-MMM-YYYY');
20 },
21
22 retrieveAll() {
23 TodoDataService.getAll()
24 .then(response => (
25 this.todoApplication = response.data;
26 ))
27 }
28 }
29}
- Line# 25: Replace the console output with the object to be displayed on UI.
Refresh the Vue App browser page and we will see the response coming from microservice REST API.
Display One Item
To display only one item, we need to update the following components.
- Update the
/src/TodoApplication/TodoApplication.js
1export default {
2 name: 'todo-application',
3 components: {},
4 props: [],
5 data () {
6 return {
7 todoApplication:[],
8 isSingle : false,
9 hasComments: false
10 }
11 },
12
13 computed: {},
14
15 mounted () {...},
16
17 methods: {
18 view(todoApp) {
19 this.todoApplication = todoApp
20 if(todoApp.todoTaskCommentsSet.length > 0) this.hasComments = true
21 this.isSingle = true
22 }
23 }
24}
- Update the
/src/TodoApplication/TodoApplication.html
1<section class="todo-application">
2 <div class="container">
3 <div class="container" v-show="!isSingle">
4 <table class='table-striped' border='1' align="center">
5 <thead>
6 <tr>
7 <th>Title</th>
8 <th>Description</th>
9 <th>Due Date</th>
10 <th>Status</th>
11 <th>No. Of Comments</th>
12 <th>View</th>
13 </tr>
14 </thead>
15 <tbody>
16 <tr v-for='todoApp in todoApplication'>
17 <td> {{ todoApp.title }} </td>
18 <td> {{ todoApp.description }} </td>
19 <td> {{ formatDate(todoApp.dueDate) }} </td>
20 <td> {{ todoApp.status }} </td>
21 <td v-if="todoApp.todoTaskCommentsSet != null"> {{ todoApp.todoTaskCommentsSet.length }} </td>
22 <td><button class="btn btn-success" v-on:click="view(todoApp)">VIEW</button></td>
23 </tr>
24 </tbody>
25 </table>
26 </div>
27 <!-- Start of div for displaying the single item -->
28 <div v-show="isSingle">
29 <table class='table-striped' border='1' align="center">
30 <tbody>
31 <tr>
32 <th>Title</th>
33 <td> {{todoApplication.title}} </td>
34 </tr><tr>
35 <th>Description</th>
36 <td> {{ todoApplication.description }} </td>
37 </tr><tr>
38 <th>Creation Date</th>
39 <td> {{ formatDate(todoApplication.creationDate) }} </td>
40 </tr><tr>
41 <th>Due Date</th>
42 <td> {{ formatDate(todoApplication.dueDate) }} </td>
43 </tr><tr>
44 <th>Status</th>
45 <td> {{todoApplication.status}} </td>
46 </tr><tr>
47 <th>
48 Comments
49 </th>
50 <td v-show="hasComments">
51 <table class='table-striped' border='1' align="center">
52 <thead>
53 <tr>
54 <th>Creation Date</th>
55 <th>Description</th>
56 </tr>
57 </thead>
58 <tbody>
59 <tr v-for='todoComments of todoApplication.todoTaskCommentsSet'>
60 <td>{{ formatDate(todoComments.creationDate) }}</td>
61 <td> {{todoComments.taskComments}} </td>
62 </tr>
63 </tbody>
64 </table>
65 </td>
66 </tr>
67 </tbody>
68 </table>
69 </div>
70 <!-- End of div for displaying the single item -->
71 </div>
72</section>
Refresh the Vue App browser page and we will see response similar to this:
When we click on View button, we get the below page
Update One Item
Description | CRUD Operation | HTTP Method | REST API Endpoint |
---|---|---|---|
Update One Specific Todo Task | UPDATE | PUT | /tasks |
- Update
/src/TodoApplication/TodoDataService.js
1getStatus() {
2 return http.get("/status");
3}
4
5update(data){
6 return http.put('',data);
7}
- Update the
/src/TodoApplication/TodoApplication.js
1export default {
2 name: 'todo-application',
3 components: {},
4 props: [],
5 data () {
6 return {
7 todoApplication:[],
8 todoStatus: [],
9 isSingle : false,
10 hasComments: false,
11 isEdit : false,
12 showToggleCommentTable: false
13 }
14 },
15
16 computed: {},
17
18 mounted () {
19 this.retrieveAll();
20 this.retrieveStatus();
21 },
22
23 methods: {
24 retrieveStatus() {
25 TodoDataService.getStatus()
26 .then(response => (this.todoStatus = response.data))
27 },
28
29 edit(todoApp) {
30 this.todoApplication = todoApp;
31 this.isSingle = true;
32 this.isEdit = true;
33 this.todoTaskComments = {todoTaskCommentsId:null, taskComments:'', creationDate: null};
34 },
35
36 submit(todoApplication, todoTaskComments) {
37 var todoTaskCommentsArray= [];
38 todoTaskCommentsArray.push(todoTaskComments);
39 todoApplication.todoTaskCommentsSet=todoTaskCommentsArray;
40
41 TodoDataService.update(todoApplication);
42 location.reload();
43 },
44
45 toggleCommentsTable(){
46 this.showToggleCommentTable = !this.showToggleCommentTable;
47 }
48 }
49}
- Update the
/src/TodoApplication/TodoApplication.html
- We add Edit button on the list view div
- We update the single view div by separtaing the view and edit sections.
1<section class="todo-application">
2 <div class="container">
3 <div class="container" v-show="!isSingle">
4 <table class='table-striped' border='1' align="center">
5 <thead>
6 <tr>
7 <th>Title</th>
8 <th>Description</th>
9 <th>Due Date</th>
10 <th>Status</th>
11 <th>No. Of Comments</th>
12 <th>View</th>
13 <th>Edit</th>
14 </tr>
15 </thead>
16 <tbody>
17 <tr v-for='todoApp in todoApplication'>
18 <td> {{ todoApp.title }} </td>
19 <td> {{ todoApp.description }} </td>
20 <td> {{ formatDate(todoApp.dueDate) }} </td>
21 <td> {{ todoApp.status }} </td>
22 <td v-if="todoApp.todoTaskCommentsSet != null"> {{ todoApp.todoTaskCommentsSet.length }} </td>
23 <td><button class="btn btn-success" v-on:click="view(todoApp)">VIEW</button></td>
24 <td><button class="btn btn-success" v-on:click="edit(todoApp)">EDIT</button></td>
25 </tr>
26 </tbody>
27 </table>
28 </div>
29 <!-- Start of div for displaying the single item -->
30 <div v-show="isSingle">
31 <table class='table-striped' border='1' align="center">
32 <tbody>
33 <tr>
34 <th>Title</th>
35 <td v-if="!isEdit"> {{todoApplication.title}} </td>
36 <td v-if="isEdit"><input v-model="todoApplication.title" size="35" /></td>
37 </tr><tr>
38 <th>Description</th>
39 <td v-if="!isEdit"> {{ todoApplication.description }} </td>
40 <td v-if="isEdit"><textarea v-model="todoApplication.description" rows="3" cols="38"></textarea></td>
41 </tr><tr>
42 <th>Creation Date</th>
43 <td> {{ formatDate(todoApplication.creationDate) }} </td>
44 </tr><tr>
45 <th>Due Date</th>
46 <td v-if="!isEdit"> {{ formatDate(todoApplication.dueDate) }} </td>
47 <td v-if="isEdit"><input type="date" v-model="todoApplication.dueDate" /></td>
48 </tr><tr>
49 <th>Status</th>
50 <td v-if="!isEdit"> {{todoApplication.status}} </td>
51 <td v-if="isEdit">
52 <select v-model="todoApplication.status">
53 <option value="">--Select Status--</option>
54 <option v-for="status of todoStatus" v-bind:value="status">{{status}}</option>
55 </select>
56 </td>
57 </tr><tr>
58 <th>
59 Comments
60 </th>
61 <td v-show="isEdit || hasComments">
62 <table class='table-striped' border='1' align="center">
63 <thead>
64 <tr>
65 <th>Creation Date</th>
66 <th>Description</th>
67 </tr>
68 </thead>
69 <tbody>
70 <tr v-for='todoComments of todoApplication.todoTaskCommentsSet'>
71 <td>{{ formatDate(todoComments.creationDate) }}</td>
72 <td> {{todoComments.taskComments}} </td>
73 </tr>
74 <tr v-if="isEdit">
75 <td colspan="2"><button class="btn btn-success" v-on:click="toggleCommentsTable()">Add New Comments</button></td>
76 </tr>
77 <tr>
78 <td colspan="2">
79 <table v-if="showToggleCommentTable">
80 <tr>
81 <th>Description</th>
82 <td>
83 <textarea v-model="todoTaskComments.taskComments" rows="2" cols="24"></textarea>
84 </td>
85 </tr>
86 </table>
87 </td>
88 </tr>
89 </tbody>
90 </table>
91 </td>
92 </tr><tr v-if="isEdit">
93 <td colspan="2"><button class="btn btn-success" v-on:click="submit(todoApplication, todoTaskComments)">SUBMIT</button></td>
94 </tr>
95 </tbody>
96 </table>
97 </div>
98 <!-- End of div for displaying the single item -->
99 </div>
100</section>
Refresh the Vue App browser page and we will see response similar to this:
When we click on View button, we get the below page
On clicking ADD NEW COMMENTS button
On clicking SUBMIT button, after updates and adding new Comment
Delete one item from the list
Description | CRUD Operation | HTTP Method | REST API Endpoint |
---|---|---|---|
Delete One Specific Todo Task | DELETE | DELETE | /tasks/{id} |
- Update
/src/TodoApplication/TodoDataService.js
1deleteById(id){
2 return http.delete(`/${id}`);
3}
- Update the
/src/TodoApplication/TodoApplication.js
1export default {
2 name: 'todo-application',
3 components: {},
4 props: [],
5 data () {
6 return {...}
7 },
8
9 computed: {},
10
11 mounted () {...},
12
13 methods: {
14 deleteById(todoApp) {
15 TodoDataService.deleteById(todoApp.systemTasksId)
16 location.reload();
17 }
18 }
19}
- Update the
/src/TodoApplication/TodoApplication.html
1<section class="todo-application">
2 <div class="container">
3 <div class="container" v-show="!isSingle">
4 <table class='table-striped' border='1' align="center">
5 <thead>
6 <tr>
7 <th>Title</th>
8 <th>Description</th>
9 <th>Due Date</th>
10 <th>Status</th>
11 <th>No. Of Comments</th>
12 <th>View</th>
13 <th>Edit</th>
14 <th>Delete</th>
15 </tr>
16 </thead>
17 <tbody>
18 <tr v-for='todoApp in todoApplication'>
19 <td> {{ todoApp.title }} </td>
20 <td> {{ todoApp.description }} </td>
21 <td> {{ formatDate(todoApp.dueDate) }} </td>
22 <td> {{ todoApp.status }} </td>
23 <td v-if="todoApp.todoTaskCommentsSet != null"> {{ todoApp.todoTaskCommentsSet.length }} </td>
24 <td><button class="btn btn-success" v-on:click="view(todoApp)">VIEW</button></td>
25 <td><button class="btn btn-success" v-on:click="edit(todoApp)">EDIT</button></td>
26 <td><button class="btn btn-warning" v-on:click="deleteById(todoApp)">DELETE</button></td>
27 </tr>
28 </tbody>
29 </table>
30 </div>
31 </div>
32</section>
Refresh the Vue App browser page and we will see response similar to this:
After deleting the entry
Create New Item
Description | CRUD Operation | HTTP Method | REST API Endpoint |
---|---|---|---|
Create New Todo Task | CREATE | POST | /tasks |
- Update
/src/TodoApplication/TodoDataService.js
1create(data){
2 return http.post('',data);
3}
- Update the
/src/TodoApplication/TodoApplication.js
1export default {
2 name: 'todo-application',
3 components: {},
4 props: [],
5 data () {
6 return {
7 todoApplicationNew: {systemTasksId:0,title:'', description:'', dueDate: null, status:''},
8 isCreate: false,
9 todoStatus: []
10 }
11 },
12
13 computed: {},
14
15 mounted () {...},
16
17 methods: {
18 retrieveStatus() {
19 TodoDataService.getStatus()
20 .then(response => (this.todoStatus = response.data))
21 },
22
23 create() {
24 this.isCreate = true;
25 },
26
27 createSubmit(data) {
28 TodoDataService.create(data);
29 location.reload();
30 }
31 }
32}
- Update the
/src/TodoApplication/TodoApplication.html
1<section class="todo-application">
2 <div class="container">
3 <div class="container" v-show="!isCreate && !isSingle">
4 <button class="btn btn-success" v-on:click="create()">CREATE</button>
5 <!-- div for displaying the list -->
6 </div>
7 <!-- div for displaying the single item -->
8 <!-- Start of div for displaying the create new item -->
9 <div v-show="isCreate">
10 <table class='table-striped' border='1' align="center">
11 <tbody>
12 <tr>
13 <th>Title</th>
14 <td><input v-model="todoApplicationNew.title" size="35" /></td>
15 </tr><tr>
16 <th>Description</th>
17 <td><textarea v-model="todoApplicationNew.description" rows="3" cols="38"></textarea></td>
18 </tr><tr>
19 <th>Due Date</th>
20 <td><input type="date" v-model="todoApplicationNew.dueDate" /></td>
21 </tr><tr>
22 <th>Status</th>
23 <td>
24 <select v-model="todoApplicationNew.status">
25 <option value="">--Select Status--</option>
26 <option v-for="status of todoStatus" v-bind:value="status">{{status}}</option>
27 </select>
28 </td>
29 </tr><tr>
30 <td colspan="2"><button class="btn btn-success" v-on:click="createSubmit(todoApplicationNew)">SUBMIT</button></td>
31 </tr>
32 </tbody>
33 </table>
34 </div>
35 <!-- End of div for displaying the create new item -->
36 </div>
37</section>
When the browser refreshes, if your updates are fine, you should see page similar to the following.
On clicking CREATE button
After Submission
Conclusion
With this setup complete, we have come to the end of this article.
At the end of this article, we have learned how to:
- Develop a Frontend MVC Application using VueJS Framework.
- How to call and consume REST APIs of microservice, from application developed using VueJS Framework.
Complete code for this project can be found at GitHub here. Go ahead and clone it.
Instructions on how to clone and run the project are provided on the GitHub page.