Skip to main content


By Muthu Kumaran - June 20, 2017

Vue.js - Simple Todo App - Part 1

PART 1: No Components, No State Management, just a simple Todo App.

If you are new to Vue.js, you can get started from the official Vue.js site. If you know JavaScript, it wouldn't take much time to get started.

Note that Vue.js only supports ES5-compliant browsers (IE8 and below are not supported).

Update:

Comparison of Vue.js with Other Frameworks

Will Vue.js Become a Giant Like Angular or React?


Hello World

First, let's just build a simple hello world using Vue.js to understand how Vue works. Here's the step by step

  1. Create an index.html and include Vue.js file

    <script src="https://unpkg.com/vue"></script>

    and the markup

    
      <div id="app">
        <p>{{ message }}</p>
      </div>
    
  2. Create a Vue instance with Vue constructor to print Hello World! in the page,

    
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello World!'
      }
    })
              

    el - provide the DOM element to mount. Accepts CSS selector or HTMLElement. Let's mount by providing div ID #app.

    data - The data object for the Vue instance. This should be an object. Vue will convert this property into reactive property.

    In components, this should be a function not object. Don't worry about this now.

    To know more about the syntax, visit Vue.js docs The Vue Instance and API reference

  3. Now open the index.html in the browser and you should see Hello World! message.

    See the Pen Hello World - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

That's all about Hello World! App. Now let's build a simple Todo app.

Simple Todo App

In Todo App, we are going to implement the following

Note: In this demo, I won't be using Components or State Management. I'll cover those in another article. This is a simple Todo App with Vue.js



Add Todo

Create a simple HTML and Vue instance


<script src="https://unpkg.com/vue"></script>

<div id="todoApp">
  <h3> {{message}} </h3>
  <form name="todo-form" method="post" action="">
    <input name="add-todo" type="text" />
    <button type="submit">Add</button>
  </form>
</div>
    

var todoApp = new Vue({
  el: '#todoApp',
  data: {
    message: 'Welcome to Todo App',
  }
})
    

See the Pen Add Form - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

v-model

Use v-model to create a two-way data binding for the input textbox. Whenever we type into the textbox v-model will automatically update Vue data object.

Now update the <input> textbox with the v-model


<input name="add-todo" type="text" v-model="addTodoInput"/>
      

Also we need to add the v-model name to Vue data object


var todoApp = new Vue({
  el: '#todoApp',
  data: {
    message: 'Welcome to Todo App',
    addTodoInput: '' // <--- add here
  }
})
      

For testing out two-way data binding, lets put {{addTodoInput}} anywhere in the markup. Whenever I type, input value will print in the browser like a live update.

Go ahead and type something in the below codepen,

See the Pen Add Form - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

To learn more about inputs and forms, visit Vue docs Form Input Bindings

Event Handling

Event Handling was made very easy in Vue.js. Use v-on directive to bind events.


        //For eg,

        // <!-- simple click event -->
        <button v-on:click="someMethod"> </button>

        // <!-- shorthand for v-on: -->
        <button @click="someMethod"> </button>

        <form v-on:submit="submitForm($event)"> </form> //$event is a special variable similar to `event` in JavaScript

        // <!-- the submit event will no longer reload the page -->
        <form v-on:submit.prevent="onSubmit"></form> //`.prevent` is a event modifier which is same as `event.preventDefault()` and `.stop` for event.stopPropagation()

        // <!-- modifiers can be chained -->
        <a v-on:click.stop.prevent="doThat"></a>

        // <!-- the click event will be triggered at most once -->
        <a v-on:click.once="doThis"></a>

        // <!-- event will triggered on hitting <enter> key -->
        <input v-on:keyup.enter="submit">

        // <!-- same as above and triggered when the keyCode is 13 -->
        <input v-on:keyup.13="submit">

        // <!-- Alt + C -->
        <input v-on:keyup.alt.67="clear">
      

There are more about events and event modifiers. See Vue docs Event Handling

Form Submit

Back to Todo app, let's add onsubmit event to the <form> tag and we will add .prevent which does is event.preventDefault()


        <form name="todo-form" method="post" action="" v-on:submit.prevent="addTask">
      

addTask is method to create new todo's. Let's create this method in Vue instance,

lists is a Vue data object, to store all Todo's items. Let's add that one to data object.


var todoApp = new Vue({
  el: '#todoApp',
  data: {
    message: 'Welcome to Todo App',
    addTodoInput: '',
    lists: [], // this will hold all the created todo task items
  },
  methods:{
    addTask: function(){
      //form submit action goes here
    }
  }
})
        

This is my lists structure,


      [
        {
          id: 1,              // Unique identifier
          title: 'Go Home',   // Todo's title
          isComplete: true    // Default: false. Mark as complete with a strike-through. We will see this later
        },
        {
          id: 2,
          title: 'Pack Bag',
          isComplete: false
        }
      ]
      

Now let's make the form submit using addTask() and add the new item to the lists

I've updated the addTask() method to update the lists reactive property


addTask: function(){

  this.lists.push({
    id: this.lists.length+1,
    title: this.addTodoInput,
    isComplete: false
  });

  this.addTodoInput = ''; //clear the input after successful submission
}
      

Check here in the codepen. You should able to add new Todo's,

See the Pen Form Submit - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

I know there is nothing happens after hitting the "Add" button. But in the background lists is getting updated with newly added todo items. Let's check in the codepen console (Make sure you are inspecting on codepen result/white area)

You can see items are getting updated in the items. Next step is to list all Todo Items in the page.

List all Todo Items

We can use the v-for directive to render a list of items based on an array (you can iterate objects). The v-for directive requires a special syntax in the form of list in lists, where list is the source data array and item is an alias for the array element being iterated on:


      <li v-for="list in lists" :key="list.id">
      

:key - It is recommended to provide a key with v-for if you are updating the lists. key is not needed if it's a simple loop.

Let's update the markup to list all todo items,


        <div class="todo-lists" v-if="lists.length">
          <h3>My Todo Tasks</h3>
          <ul>
            <li v-for="list in lists" :key="list.id">
              <span class="title" v-bind:class="{completed: list.isComplete}">{{list.title}}</span>
            </li>
          </ul>
        </div>
      

Go ahead and see it yourself,

See the Pen List all items - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

This works cool but we have an issue here. There is no validation when adding new todo items even empty items are appearing in the list. So lets a simple validation and make sure empty are not getting submitted.

When we hit the Add button without typing anything into textbox then we will show a red border textbox which will indicate textbox must be filled. I'm adding a new property called hasError in Vue data object to track textbox is filled or not


  data: {
    message: 'Welcome to Todo App',
    addTodoInput: '',
    lists: [],
    hasError: false  // <-- to handle errors
  },
      

Update the addTask method to update hasError property,


  addTask: function(){

    if(!this.addTodoInput){ // <--- If no value then we are setting error to `true`
      this.hasError = true;
      return;               // <--- stops here
    }

    this.hasError = false; // <--- If textbox is filled then setting error to `false`

    this.lists.push({
      id:this.lists.length+1,
      title: this.addTodoInput,
      isComplete: false
    });

    this.addTodoInput = '';
  }

Let's update the textbox to show red border when we simply hitting the button. We will use v-bind:class to attach error class,


<input name="add-todo" type="text" v-model="addTodoInput" v-bind:class="{error: hasError}"/>

Now check out this codepen, you should able to see the simple validation,

See the Pen List all items - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

Update Todo

To update the existing create Todo item, let's update the markup


        // Updating this <span> tag
        <span class="title" v-bind:class="{completed: list.isComplete}">{{list.title}}</span>

        to

        <span class="title"
          contenteditable="true"
          v-on:keydown.enter="updateTask($event, list)"
          v-on:blur="updateTask($event, list)"
          v-bind:class="{completed: list.isComplete}"> {{list.title}} </span>
      

I'm using contenteditable="true" to edit the field which was easier than putting a textbox. I'm trying to mimic <span> tag as textbox which I able to do it with the help of CSS and JavaScript.

v-on:keydown.enter - This event will trigger when hitting the ENTER key and execute updateTask() method.

v-on:blur - This event will trigger during blur and execute updateTask() method.

Let's add updateTask to Vue methods and update the edited list.


        methods:{
          ...
          ...
          updateTask: function(e, list){
            e.preventDefault();
            list.title = e.target.innerText;
            e.target.blur();
          }
        }
      

Here's the demo in codepen and now try editing the item from the Todo list,

See the Pen Update items - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

Mark as Complete

This is another feature I like to add to Todo app. If the task is complete let's strike out the completed task.

Let's add a <checkbox> to each Todo item task. When checking the <checkbox> will strike-out the task.



        <li v-for="list in lists" :key="list.id">
          <input type="checkbox" v-on:change="completeTask(list)" v-bind:checked="list.isComplete"/>
        ...
        </li>

      

We have attached completeTask method to change event which will update list.isComplete to true/false.

v-bind:checked - will check or uncheck checkbox based on the condition of list.isComplete

Adding completeTask to Vue methods,



        methods:{
          ...
          ...
          completeTask: function(list){
            list.isComplete = !list.isComplete;
          }
        }

      

Here's the demo in codepen and now try checking the checkbox in Todo list,

See the Pen Mark as Complete - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

Demo looks great but now I want to move completed items below and active items to top. So that it's easy to manage the Todo App Task list. To bring the active items top, let's sort the lists by isComplete: false

Now we need to filter or sort the data without mutating the lists data object. Let's create an another method to filter the data but this method should go inside computed property.

Why Computed property instead of Methods?

It's better to read it from Vue docs, Computed Properties

The main reason to go with computed property is "computed properties are cached based on their dependencies". We can still define the same method to Methods property but when template rendered each time, the method will execute each time. A computed property will only re-evaluate when some of its dependencies have changed. As long as lists data object is not changed, computed property will immediately return the previously computed result without having to run the function again.

Let's update the JS and add computed property



        data: {
          ...
        },
        computed: {
          filterLists: function(){
            return _.orderBy(this.lists, ['isComplete', false])
          }
        },
        methods:{
          ...
        }
        

filterLists methods will return sorted list with active at top and completed items at the bottom.

I'm using lodash library to perform array manipulation. _.orderBy will return sorted lists

Make sure you include lodash library before testing the code,


          <script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
        

To make this work, we need to make one more change in the markup. We need to change v-for loop which uses lists to iterate


          //Change
          <li v-for="list in lists" :key="list.id">

            to

          //replace `lists` with `filterLists`
          <li v-for="list in filterLists" :key="list.id">

        

Here's the codepen demo to test it out. Try adding more Todo items and check the checkbox to see the magic,

See the Pen Mark as Complete with sort - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

Delete Todo

Next is, we are going to delete a Todo item. Let's add the below delete icon in the markup

<span class="remove" v-on:click="removeTask(list)">x</span>

removeTask - method will remove todo item from lists data object.


          methods:{
            ...
            ...
            removeTask: function(list){
              var index = _.findIndex(this.lists, list);
              this.lists.splice(index, 1);
            }
          }
        

Check out the codepen for deleting a Todo item,

See the Pen Remove Item - Demo Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

The Final Demo

Finally, we have a built a simple Todo App using Vue.js. Here's the complete HTML, CSS, and JavaScript to play around.

HTML/CSS

See the Pen Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

JavaScript

See the Pen Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

Final Demo

See the Pen Todo App - Vue.js by Muthu Kumaran (@mkumaran) on CodePen.

What's Next?

In next article, I'll show you how to use Components and State Management with the same Todo App. Once I complete the write-up, I'll share the link here.

Update:

Continue to read Part 2 here, Vue.Js - Todo App UUsing Vuex - PART 2.



If you like this, please share



Comments


Thank you for visiting my page. Please shares your views and suggestion in the comment box below.