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