How to Create a Simple Notes Application with Vue.js

By Diogo Souza

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.



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •  
Thanks for your registration, follow us on our social networks to keep up-to-date