Skip to main content

Vue.Js CRUD Application

vue-js-crud-app-feature-image

Vue.js 2 CRUD Application with Vue Router & Axios


In this tutorial, I will show you how to build a Vue.js 2 CRUD Application to consume REST APIs, display and modify data using Vue 2, Vue Router and Axios.

Overview of Vue.js 2 CRUD Application

We will build a Vue.js front-end Tutorial Application in that:
  • Each Tutorial has id, title, description, published status.
  • We can create, retrieve, update, delete Tutorials.
  • There is a Search bar for finding Tutorials by title.
Here are screenshots of our Vue.js 2 CRUD Application.
– Create a Tutorial:
vue-js-crud-example-create-item
– Retrieve all Tutorials:
vue-js-crud-example-retrieve-items
– Click on Edit button to update a Tutorial:
vue-js-crud-example-update-item
On this Page, you can:
  • change status to Published using Publish button
  • delete the Tutorial using Delete button
  • update the Tutorial details with Update button
– Search Tutorials by title:
vue-js-crud-example-find-items
The introduction above is for Vue.js Client with assumption that we have a Server exporting REST APIs:
MethodsUrlsActions
POST/api/tutorialscreate new Tutorial
GET/api/tutorialsretrieve all Tutorials
GET/api/tutorials/:idretrieve a Tutorial by :id
PUT/api/tutorials/:idupdate a Tutorial by :id
DELETE/api/tutorials/:iddelete a Tutorial by :id
DELETE/api/tutorialsdelete all Tutorials
GET/api/tutorials?title=[keyword]find all Tutorials which title contains keyword
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/PostgreSQL
– Spring Boot & MongoDB
– Spring Boot & Cassandra
All of them can work well with this Vue App.

Vue.js App Component Diagram with Vue Router & Axios

vue-js-crud-app-overview
– The App component is a container with router-view. It has navbar that links to routes paths.
– TutorialsList component gets and displays Tutorials.
– Tutorial component has form for editing Tutorial’s details based on :id.
– AddTutorial component has form for submission new Tutorial.
– These Components call TutorialDataService methods which use axios to make HTTP requests and receive responses.


Technology
  • vue: 2.6.10
  • vue-router: 3.1.3
  • axios: 0.19.0

Project Structure

vue-js-crud-app-project-structure
Let me explain it briefly.
– package.json contains 3 main modules: vuevue-routeraxios.
– There are 3 components: TutorialsListTutorialAddTutorial.
– router.js defines routes for each component.
– http-common.js initializes axios with HTTP base Url and headers.
– TutorialDataService has methods for sending HTTP requests to the Apis.
– vue.config.js configures port for this Vue Client.

Setup Vue.js Project

Open cmd at the folder you want to save Project folder, run command:
vue create vue-js-client-crud
You will see some options, choose default (babel, eslint).
After the process is done. We create new folders and files like the following tree:

 public
 index.html
 src
 components
 AddTutorial.vue
 Tutorial.vue
 TutorialsList.vue
 services
 TutorialDataService.js
 App.vue
 main.js
 package.json

Open public/index.html, add bootstrap inside <head> tag:
<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <title>vue-js-client-crud</title>
    <link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
  </head>
  <body>
    ...
  </body>
</html>

Add Vue Router to Vue.js 2 CRUD App

– Run the command: npm install vue-router.
– In src folder, create router.js and define Router as following code:
import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

export default new Router({
  mode: "history",
  routes: [
    {
      path: "/",
      alias: "/tutorials",
      name: "tutorials",
      component: () => import("./components/TutorialsList")
    },
    {
      path: "/tutorials/:id",
      name: "tutorial-details",
      component: () => import("./components/Tutorial")
    },
    {
      path: "/add",
      name: "add",
      component: () => import("./components/AddTutorial")
    }
  ]
});
– Open src/main.js, then import router:
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

Add Navbar and Router View to Vue.js 2 CRUD App

Let’s open src/App.vue, this App component is the root container for our application, it will contain a navbar.
<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>

    <div class="container mt-3">
      <router-view />
    </div>
  </div>
</template>

<script>
export default {
  name: 'app'
}
</script>

Initialize Axios for Vue.js 2 CRUD HTTP Client

Now we’re gonna install axios with command: npm install axios.
Then, under src folder, we create http-common.js file like this:
import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-type": "application/json"
  }
});
Remember to change the baseURL, it depends on REST APIs url that your Server configures.

Create Data Service

Our service will use axios from HTTP client above to send HTTP requests.
services/TutorialDataService.js
import http from "../http-common";

class TutorialDataService {
  getAll() {
    return http.get("/tutorials");
  }

  get(id) {
    return http.get(`/tutorials/${id}`);
  }

  create(data) {
    return http.post("/tutorials", data);
  }

  update(id, data) {
    return http.put(`/tutorials/${id}`, data);
  }

  delete(id) {
    return http.delete(`/tutorials/${id}`);
  }

  deleteAll() {
    return http.delete(`/tutorials`);
  }

  findByTitle(title) {
    return http.get(`/tutorials?title=${title}`);
  }
}

export default new TutorialDataService();

Create Vue Components

As I’ve said before, we have 3 components corresponding to 3 routes defined in Vue Router.

Add item Component

This component has a Form to submit new Tutorial with 2 fields: title & description. It calls TutorialDataService.create() method.
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>

      <button @click="saveTutorial" class="btn btn-success">Submit</button>
    </div>

    <div v-else>
      <h4>You submitted successfully!</h4>
      <button class="btn btn-success" @click="newTutorial">Add</button>
    </div>
  </div>
</template>

<script>
import TutorialDataService from "../services/TutorialDataService";

export default {
  name: "add-tutorial",
  data() {
    return {
      tutorial: {
        id: null,
        title: "",
        description: "",
        published: false
      },
      submitted: false
    };
  },
  methods: {
    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>
.submit-form {
  max-width: 300px;
  margin: auto;
}
</style>

List of items Component

This component calls 3 TutorialDataService methods:
  • 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>

      <button class="m-3 btn btn-sm btn-danger" @click="removeAllTutorials">
        Remove All
      </button>
    </div>
    <div class="col-md-6">
      <div v-if="currentTutorial">
        <h4>Tutorial</h4>
        <div>
          <label><strong>Title:</strong></label> {{ currentTutorial.title }}
        </div>
        <div>
          <label><strong>Description:</strong></label> {{ currentTutorial.description }}
        </div>
        <div>
          <label><strong>Status:</strong></label> {{ currentTutorial.published ? "Published" : "Pending" }}
        </div>

        <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>
import TutorialDataService from "../services/TutorialDataService";

export default {
  name: "tutorials-list",
  data() {
    return {
      tutorials: [],
      currentTutorial: null,
      currentIndex: -1,
      title: ""
    };
  },
  methods: {
    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;
    },

    setActiveTutorial(tutorial, index) {
      this.currentTutorial = tutorial;
      this.currentIndex = index;
    },

    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>
.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.

Item details Component

For getting data & update, delete the Tutorial, this component will use 3 TutorialDataService methods:
  • 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>

    <button class="badge badge-danger mr-2"
      @click="deleteTutorial"
    >
      Delete
    </button>

    <button type="submit" class="badge badge-success"
      @click="updateTutorial"
    >
      Update
    </button>
    <p>{{ message }}</p>
  </div>

  <div v-else>
    <br />
    <p>Please click on a Tutorial...</p>
  </div>
</template>

<script>
import TutorialDataService from "../services/TutorialDataService";

export default {
  name: "tutorial",
  data() {
    return {
      currentTutorial: null,
      message: ''
    };
  },
  methods: {
    getTutorial(id) {
      TutorialDataService.get(id)
        .then(response => {
          this.currentTutorial = response.data;
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    },

    updatePublished(status) {
      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.currentTutorial)
        .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>
.edit-form {
  max-width: 300px;
  margin: auto;
}
</style>

Configure Port for Vue.js 2 CRUD App

Because most of HTTP Server use CORS configuration that accepts resource sharing retricted to some sites or ports, so we also need to configure port for our App.
In src folder, create vue.config.js file with following content:
module.exports = {
  devServer: {
    port: 8081
  }
}
We’ve set our app running at port 8081.

Run Vue.js 2 CRUD App

You can run our App with command: npm run serve.
If the process is successful, open Browser with Url: http://localhost:8081/ and check it.
This Vue Client will work well with following back-end Rest APIs:
– Express, Sequelize & MySQL
– Express, Sequelize & PostgreSQL
– Express & MongoDb
– Spring Boot & MySQL/PostgreSQL
– Spring Boot & MongoDB

Source Code

You can find the complete source code for this tutorial on Github.

Conclusion

Today we’ve built a Vue.js 2 CRUD Application successfully with Vue Router & Axios. Now we can consume REST APIs, display and modify data in a clean way. I hope you apply it in your project at ease.
Happy learning, see you again!

Further Reading





Note: This is only for Learning and Documentation purposes. 

Comments

Popular posts from this blog

Reset 120 day RDS Grace period on 2016 and 2019

  Reset 120 day RDS Grace period on 2016 and 2019 Enter the following command to check Grace Period: wmic /namespace:\\root\CIMV2\TerminalServices PATH Win32_TerminalServiceSetting WHERE (__CLASS !=””) CALL GetGracePeriodDays  Confirm-deletion-of-the-timebomb-key-in-the-registry If you have a home lab environment or another lab where you continually test various solutions, licensing, and trial expiration is a challenge that you constantly tend to run into. It is just part of the fun of lab environments. While most trials are fairly “hard and fast” and don’t allow you to reset the trial expiration, if you work with Microsoft Windows Server and Remote Desktop Services (RDS), there is a “hack” that allows you to effectively reset the expiration of  Remote Desktop  Services grace period where you can essentially rewind the clock on your RDS licensing if you are making use of this role inside your lab environment. I am using Windows Server 2019 for my Windows workloads in...

Remote Desktop Services session timeout Setup in RD Session Host in Windows Servers

  If you face a session time-limit policy issue that gets disconnected in the meantime. You can follow the steps below in order to fix the issue, By default, the user’s RDP session in Windows may stay disconnected until the user or administrator terminates, or the computer is restarted. However, it is quite convenient since a user may connect to his old RDP session and go on working with running programs. In order to terminate disconnected RDP/RDS sessions automatically in a specified time period, you need to set session limits (time-outs) correctly. If you are using an RDS server, you will have to configure session time-out parameters from the RDS collection settings in the Session tab menu. You will have to Specify the time period after which you want to disconnect the RDP session. Lastly, a disconnected session option (by default, a session period is unlimited – Never). Thus, you can set the maximum duration of an active session (Active session limit) and end an idle session (Id...

Peachtree Auto Backup Using Task Scheduler with images

Before creating an auto backup with Task Scheduler.  1. In PEACHTREE Application, Create an initial Auto Backup. Click Browse to locate folder "SAVE BACKUP TO:" Enter "USERNAME" and "PASSWORD" Follow everything on the image Click "Save As" to your backup folder Then "Run Backup" and copy the PROGRAM SCRIPT  at the bottom to be used later, see image below. Search and open "TASK SCHEDULER" 1. Click the tab "GENERAL" Followup the setup on the picture. 2. Click the tab "TRIGGERS". Then Click the New button to create a schedule. After setting up the schedule. Click OK. 3. To Create new Actions, click the tab "ACTIONS", click NEW then paste the PROGRAM/SCRIPT below. Then Click OK. 4. Next click the tab "CONDITIONS" and follow everything in the picture. 5. Click the tab "SETTING"  and follow everything in the picture.    6. Lastly right-click the folder "TASK SCHEDULER LIBRARY...