Vue.js CRUD example – a simple Note App

vue-js-crud-example-a-simple-note-app-feature-image

In this tutorial, we’re gonna create a simple Note App that represents Vue.js CRUD example with Vue CLI 3 for building project.

More Practice:
Vue.js Firebase Database example – CRUD Operations
Vue.js Firestore example – Vue.js CRUD serverless with Firebase Cloud Firestore

Vue.js CRUD example Overview

Goal

Our Vue App can help us write new Notes, then it displays a list of Notes and each Note page (containing title and content) can be modified easily:

vuejs-crud-example-note-app-overview

Project Structure

vuejs-crud-example-note-app-project-structure

We have 3 components:
App.vue holds all of the other components.
NotesList.vue contains all of notes in a List with + Note button.
Note.vue display a single Note in the List that allows us to create new Note or edit current Note.

Technologies

– Vue CLI 3.0.1
– Vue 2.5.17

Practice

Setup Project

Install vue-cli

For use Vue CLI anywhere, run command:
npm install -g vue-cli

Init Project

Point cmd to the folder you want to save Project folder, run command:
vue create vue-note-app

You will see 2 options, choose default:

vuejs-crud-example-note-app-project-setup-1

Build Components

Note Component

Inside components folder, create Note.vue file that will contain title field and content field of a Note:

<template>
   <div class="note">
        <div v-if="note">
            <h3>Note</h3>
            <div class="form-group">
                <input class="form-control" type="text" v-model="note.title" placeholder="Title" />
            </div>
            <div class="form-group">
                <textarea class="form-control" v-model="note.content" placeholder="Content"></textarea>
            </div>
            <button class="btn btn-danger" @click="removeNote()">Remove</button>
        </div>
        <div v-else>
            <h5>Please create new Note...</h5>
        </div>
        
    </div> 
</template>

<script>
export default {
  name: "Note",
  props: ["note"],
  methods: {
    removeNote() {
      this.$emit("app-removeNote");
    }
  }
};
</script>

<style>
.note {
  margin: 20px;
}
</style>

Let’s describe the code above:
– First we check if we have valid Note. If not, it show a message telling the user to create new Note:

<div v-if="note">
    <!-- display Form Data --->
</div>
<div v-else>
    <h5>Please create new Note...</h5>
</div>

– If we have a Note to work with, we bind two modals: note.title & note.content.
– Then we have a Remove button to handle removing the current Note.

<input type="text" v-model="note.title" placeholder="Title" />
<textarea v-model="note.content" placeholder="Content"></textarea>

<button class="btn btn-danger" @click="removeNote()">Remove</button>

– We define a property props: ["note"] and a method that emits event to the parent component:

export default {
  name: "Note",
  props: ["note"],
  methods: {
    removeNote() {
      this.$emit("app-removeNote");
    }
  }
};

NotesList Component

Inside components folder, create NotesList.vue file that handles the list of Notes:

<template>
    <div class="list">
        <h3>List</h3>
        <ul class="list-group">
            <li class="list-group-item"
                v-for="(note, index) in notes"
                :key="note.index"
                :class="{ 'active': index === activeNote}"
                @click="changeNote(index)"
                >
                <div>{{ note.title }}</div>
            </li>
        </ul>
        <button @click="addNote()" class="btn btn-info">+ Note</button>
    </div>
</template>

<script>
export default {
  name: "NoteList",
  props: ["notes", "activeNote"],
  methods: {
    changeNote(index) {
      this.$emit("app-changeNote", index);
    },
    addNote() {
      this.$emit("app-addNote");
    }
  }
};
</script>

<style>
.list {
  margin: 20px;
}
</style>

– First we iterate over each Note using v-for directive, assign active class if that Note is in use add a click listener that calls changeNote() when clicked on.
We also have a separate click listener that call addNote() for creating new Note.

<ul>
    <li
        v-for="(note, index) in notes"
        :key="note.index"
        :class="{ 'active': index === activeNote}"
        @click="changeNote(index)"
        >
        <div>{{ note.title }}</div>
    </li>
</ul>

<button @click="addNote()" class="btn btn-info">+ Note</button>

– And the component we define is so simple. We have two properties:
+ notes array of all the Notes.
+ activeNote number for the Note that user is clicking on.
2 methods emit the event to the parent component.

export default {
  name: "NoteList",
  props: ["notes", "activeNote"],
  methods: {
    changeNote(index) {
      this.$emit("app-changeNote", index);
    },
    addNote() {
      this.$emit("app-addNote");
    }
  }
};

App Component

With our setup Project step, we have a specific structure.
Now check public/index.html file with id="app" element. We add Bootstrap, the code will be like:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta ...>
  <link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
  <title>vue-note-app</title>
</head>

<body>
  <noscript>
    <strong>We're sorry ...</strong>
  </noscript>
  <div id="app"></div>
  <!-- built files will be auto injected -->
</body>

</html>

Inside src/main.js, we can see that Vue is imported alongside the App.vue component, render the component at the element with id of #app:

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')
</pre>
Now we modify code inside <strong>App.vue</strong>:
<pre class="lang:xhtml">
<template>
    <div id="app">
        <div style="color: blue;">
            <h1>ozenero</h1>
            <h3>Note App</h3>
        </div>
        <div class="row">
            <div class="col-sm-6">
                <NotesList 
                  @app-addNote="addNote"
                  @app-changeNote="changeNote"
                  :notes="notes"
                  :activeNote="index" />
            </div>
            <div class="col-sm-6">
                <Note
                  @app-removeNote="removeNote"
                  :note="notes[index]" />
            </div>
        </div>
    </div>
</template>

<script>
import NotesList from "./components/NotesList.vue";
import Note from "./components/Note.vue";

export default {
  name: "app",
  components: {
    NotesList,
    Note
  },
  data: () => ({
    notes: [],
    index: 0
  }),
  methods: {
    addNote() {
      this.notes.push({
        title: "",
        content: ""
      });
      this.index = this.notes.length - 1;
    },
    changeNote(index) {
      this.index = index;
    },
    removeNote() {
      this.notes.splice(this.index, 1);
      this.index = this.index === 0 ? 0 : this.index - 1;
    }
  }
};
</script>

<style>
#app {
  text-align: center;
  max-width: 700px;
}
</style>

Let’s describe the code above.
– First, we import 2 components above (NotesList and Note).
– Then we define the data() function that returns array of Notes and a number for current Note’s index in the array:

export default {
  name: "app",
  components: {
    NotesList,
    Note
  },
  data: () => ({
    notes: [],
    index: 0
  }),
  ...
}

– Look at the template:

<NotesList 
  @app-addNote="addNote"
  @app-changeNote="changeNote"
  :notes="notes"
  :activeNote="index" />
<Note
  @app-removeNote="removeNote"
  :note="notes[index]" />

Please remember that we have used $emit in 2 child Components before:
+ Note Component: $emit("app-removeNote")
+ NotesList Component: $emit("app-changeNote", index) & $emit("app-addNote")

Now we have 3 event handlers with @app- prefix that point to corresponding methods inside App component:

export default {
  ...
  methods: {
    addNote() {
      // ...
    },
    changeNote(index) {
      // ...
    },
    removeNote() {
      // ...
    }
  }
};

Run and Check Result

– Run Vue.js App with command: npm run serve.
– Open browser with url: http://localhost:8080/.
– Check result:

vuejs-crud-example-note-app-overview

Source Code

vue-note-app

0 0 votes
Article Rating
Subscribe
Notify of
guest
338 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments