Vue.js is not only a JavaScript framework that brings a whole new idea of what Web development is, but it also pushes forward the way we implement and deliver applications. The Web is a constantly changing environment that demands frameworks and technologies that support innovation. We are going to develop a simple, yet full, notes app, to show what Vue.js can do.
Setting Up
In order to create projects with Vue.js, you’ll need to have Node.js and npm already installed on your computer. Once that’s done, simply type the following command in your terminal:
npm install --global vue-cli
This is enough to have the main modules of Vue.js distributed as global npm packages for all users of your OS. Next, let’s create a new app through the following command:
vue init webpack notes-app
You will then be prompted to complete some project information. See below:
Once that is all set, let’s jump into the project’s root folder and run the command:
npm install
This is necessary because we must download all the dependencies our project needs. After that, the only thing left to get the app running properly is executing the following command:
npm run dev
This will start the server locally and open the default app template at http://localhost:8080 on your local browser.
Next, let’s go directly to our project structure and into the root file /index.html
. Paste the following code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Notes App</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.7/semantic.min.css">
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.7/semantic.min.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
That’s all we need to have the libs of jQuery and Semantic.js imported on the main page. They are necessary to make the elements selecting as well as the style of some built-in components. Then, open the file App.vue
and replace its content with the following:
<template>
<div id="app">
<h1 class="ui centered header">Vue.js Notes App</h1>
<div>
<list-notes v-bind:notes="notes"></list-notes>
<create-note v-on:create-note="createNote"></create-note>
</div>
</div>
</template>
<script>
import ListNotes from './components/ListNotes';
import CreateNote from './components/CreateNotes';
export default {
name: 'app',
components: {
ListNotes,
CreateNote,
},
data() {
return {
notes: [{
title: 'Lunch at 12PM',
description: 'Call Robert to remember him.'
}, {
title: 'Gym with Jane',
description: 'She is needing your motivation...'
}, {
title: 'Call mom',
description: 'Ask her about the cats?'
}],
};
},
methods: {
createNote(note) {
this.notes.push(note);
alert('Note created!');
},
},
};
</script>
Configuring the Main Template
Here, we are configuring the main template of our home page, along with the JavaScript to enable the main actions of the page buttons — creating and listing our notes. The first lines cover the templating system recognized by Vue.js to define each tag as a component (in this case, <list-notes>
and <create-note>
). The v-bind
attribute connects its value to the data() function that provides the original values to be associated with each field. The same with v-on
, which, in turn, is responsible for the events association.
The rest of the code simply imports the modules that we will create, defines the name of the this app and the functions that will manage them. Let’s jump now our new Vue file: CreateNotes.vue
, which you’ll have to create from scratch in your project. Take a look at the code:
<template>
<div class='ui basic content center aligned segment'>
<button class='ui basic button icon' v-on:click="openForm" v-show="!isCreating">
<i class='plus icon'></i>
</button>
<div class='ui centered card' v-show="isCreating">
<div class='content'>
<div class='ui form'>
<div class='field'>
<label>Title</label>
<input v-model="titleText" type='text'>
</div>
<div class='field'>
<label>Description</label>
<input v-model="descriptionText" type='text'>
</div>
<div class='ui two buttons'>
<button class='ui basic green button' v-on:click="sendForm()">
Create
</button>
<button class='ui basic black button' v-on:click="closeForm">
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
titleText: '',
descriptionText: '',
isCreating: false,
};
},
methods: {
openForm() {
this.isCreating = true;
},
closeForm() {
this.isCreating = false;
},
sendForm() {
if (this.titleText.length > 0 && this.descriptionText.length > 0) {
const title = this.titleText;
const description = this.descriptionText;
this.$emit('create-note', {
title,
description,
});
// Clean fields after submit
this.titleText = '';
this.descriptionText = '';
this.isCreating = false;
}
},
},
};
</script>
This one is a little bit more complex. Besides everything else we’ve already seen about Vue.js and its templating associations, we basically have the form template for each note creation: a button (with a ‘plus’ icon) to open the real form, and the form itself to contain the fields and buttons to submit/cancel the operation. Note that the attribute v-show is important to set whether the whole div is going to be shown or not. And the v-model
encapsulates the model object of a note representation we’ve seen before.
Below, we have the JavaScript data and functions that will open/close the form (through the attribute ‘isCreating’ we implemented), and the one that will propagate the note creation for its own.
In order to have this work, we’ll need a new Note.vue
file (into /components
folder) that will represent each note on the screen. Let’s take a look at this content:
<template>
<div class='ui centered card'>
<div class="content" v-show="!showEditForm">
<div class='header'>
{{ note.title }}
</div>
<div class='meta italic'>
{{ note.description }}
</div>
<div class='extra content'>
<span class='right floated edit icon' v-on:click="showForm">
<i class='edit icon'></i>
</span>
<span class='right floated trash icon' v-on:click="deleteNote(note)">
<i class='trash icon'></i>
</span>
</div>
</div>
<div class="content" v-show="showEditForm">
<div class='ui form'>
<div class='field'>
<label>Title</label>
<input type='text' v-model="note.title" >
</div>
<div class='field'>
<label>Description</label>
<input type='text' v-model="note.description" >
</div>
<div class='ui two buttons'>
<button class='ui basic button' v-on:click="hideForm">
Close X
</button>
</div>
</div>
</div>
</div>
</template>
<script type="text/javascript">
export default {
props: ['note'],
data() {
return {
showEditForm: false,
};
},
methods: {
deleteNote(note) {
this.$emit('delete-note', note);
},
showForm() {
this.showEditForm = true;
},
hideForm() {
this.showEditForm = false;
},
},
};
</script>
<style scoped>
.italic {font-style: italic}
</style>
See that we’re using the card elements from SemanticUI to make them more beautiful and clean. Each value of the card is represented through the brackets style of Vue.js to print things {{}}
, which is something very characteristic of its syntax. We also have two fields that must be filled with the previous values, along with a close button. The event of deletion will happen on our next file: ListNotes.vue
, let’s see its content:
<template>
<div>
<note v-on:delete-note="deleteNote" v-for="note in notes" :note.sync="note"></note>
</div>
</template>
<script type = "text/javascript" >
import Note from './Note';
export default {
props: ['notes'],
components: {
Note
},
methods: {
deleteNote(note) {
var removeIt = confirm('Do you really wanna remove it?');
if (removeIt) {
const noteIndex = this.notes.indexOf(note);
this.notes.splice(noteIndex, 1);
alert('Note deleted!');
}
},
},
};
</script>
Our first tag of this template is set with an event listener ‘delete-note’ and assigned to function deleteNote
(the respective code is listed below). Here, we’re using two more attributes:
- v-for: this one is important to iterate over the list of notes we caught from the model object. This kind of operation is very useful and Vue.js provides a complete solution to deal with it
- :note-sync: here, we’re saying to Vue.js that it needs to reload our whole note model, in order to advise that important changes have happened
That’s it. You can rerun your project and see the final result. As a tip, I’d suggest you remove the ESLint validations, since they’ll cause a lot of compilation errors when you try to run this code for the first time. Otherwise, you can go to each of the errors/warnings pointed out and fix them. Check out the stretch you’ll have to remove at file
bin/webpack.base.conf.js
:{ test: /\.(js|vue)$/, loader: 'eslint-loader', enforce: 'pre', include: [resolve('src'), resolve('test')], options: { formatter: require('eslint-friendly-formatter') } },
Below, you can see some screen shots of our final app running.
Conclusion
Vue.js is a really fast and simple framework that allows various configurations in our Web applications, making them easier to work with and maintain. Now, is your turn to customize your notes application. Try to add more functionality and make it fun for people to use. Good luck!
About the Author
Diogo Souza works as Java Developer at Fulcrum Worldwide and has worked for companies such as Indra Company, Atlantic Institute and Ebix LA. He is also an Android trainer, speaker at events on Java and mobile world and a DevMedia consultant.