Let's build a Todo App Using Vue.js

When you are starting to learn a new programming language or framework, a TODO app is one of the first things you build, many people hate it, others love it, personally, I think it’s a great option to give you an idea of how a language/framework works. So now that I'm learning Vue.js I decided to create one.

This is the final result: TODO Vue Finished - JSFiddle

We will simply use Vue.js and NES.css for styles.

And because it is a simple app we will be using JSFiddle, here is the base project if you want to follow the tutorial: So let’s start.

As we can see, we have three tabs: HTML, Vue, and CSS. In the HTML tab, we only have a <link> to get the Press Start font and a div with a header inside. The important thing here is to point out that this is where we will have access to all the variables and methods that we will use in our Vue file.

1<link href="https://fonts.googleapis.com/css?family=Press+Start+2P" rel="stylesheet">
2<div id="app">
3 <h1>TODOS:</h1>
4</div>

While in the Vue tab we have our Vue instance and this is where all the logic of the app will be.

1new Vue({
2 el: "#app",
3 data: {
4 todos: [
5 { text: 'todo 1', done: false, id: Date.now() },
6 { text: 'todo 2', done: false, id: Date.now() + 1 }
7 ]
8 },
9 methods: {
10
11 }
12})

Within the properties that we pass to Vue instance is el and it serves to indicate to Vue in which DOM element it is going to be mounted. In the same way, we have data and guess what, it is used to store all the data that we use during the app, which in this case will only be an array of objects called todos.

Finally, we have methods, here we will put all the functions of the app.

As mentioned above, we can access all the variables defined in data inside the div and we can check it simply by adding a new variable called title and to access it we use the following syntax inside our HTML: {{variableName}}. Vue.js uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance's data.

1data: {
2 title: 'TODOS',
3 todos: [
4 { text: 'todo 1', done: false, id: Date.now() },
5 { text: 'todo 2', done: false, id: Date.now() + 1 }
6 ]
7}
1<div id="app">
2 <h1>{{title}}:</h1>
3</div>

Before coding the app logic, let’s define what the app should do:

  • It should display a list of TODOs.
  • It should add TODOs.
  • It should delete TODOs.
  • It should mark TODOs as Done.

Showing TODOs

If you remember, we already have an array of objects in data to store TODOs.

1data: {
2 title: 'TODOS',
3 todos: [
4 { text: 'todo 1', done: false, id: Date.now() },
5 { text: 'todo 2', done: false, id: Date.now() + 1 }
6 ]
7}

We want to show the TODO text and the state on a list.

1<ul>
2 <li class="flex"></li>
3</ul>

This is the base structure, now we simply have to show the array data and we can do it using the template syntax {{}}

1<ul>
2 <li class="flex"> {{todos[0].text}} </li>
3 <li class="flex"> {{todos[1].text}} </li>
4</ul>

But that is something repetitive, fortunately, Vue offers a directive called v-for that allows us to iterate over the elements of an array. So we can refactor the previous code in the following:

1<ul>
2 <!-- Remember that todos is the name of our array -->
3 <li v-for="todo in todos" class="flex">
4 <!-- and now, we can access to an item using todo-->
5 {{todo.text}}
6 </li>
7</ul>

Showing TODOs

Adding TODOs

To add TODOS we need an input field.

1<input type=“text” class=“nes-input” placeholder="Add todo…">

What we want to do is add a TODO every time the enter key is pressed. Vue allows us to listen for events using the syntax of v-on:some-event="doSomething", in this case, what we need is to use v-on:keyup.enter, here, keyup is the event we want to hear and enter the key that we need to trigger addTodo.

1<input type="text" class="nes-input" placeholder="Add todo..." v-on:keyup.enter="addTodo">

Now, we just add our method addTodo

1methods: {
2 addTodo(event) {
3 const text = event.target.value
4 this.todos.push({text, done: false, id: Date.now()})
5 event.target.value = ''
6 }
7}

We almost finished, we just need to remove TODOs and mark them as done.

  • It should display a list of TODOs
  • It should add TODOs
  • It should remove TODOs
  • It should mark TODOs as Done

Remove TODOs

The first thing we do is add a button to remove TODOs:

1<li v-for="todo in todos" class="flex">
2 <!-- and now, we can access to an item using todo-->
3 {{todo.text}}
4 <div class="space"></div>
5 <button class="nes-btn is-error padding">X</button>
6</li>

Now, what we have to do is listen for an onclick event, following the syntax for the events v-on:some-event="doSomething", we get this v-on:click ="removeTodo(todo.id)". We will call the removeTodo method and pass it the todo id. After that, we need to create that method.

1<ul>
2 <li v-for="todo in todos" class="flex">
3 {{todo.text}}
4 <div class="space"></div>
5 <button class="nes-btn is-error padding" v-on:click="removeTodo(todo.id)">X</button>
6 </li>
7</ul>
1removeTodo(id) {
2 this.todos = this.todos.filter(todo => todo.id !== id)
3}

Marking TODOs as done

Finally, we just need to mark the TODOs as done, for this, we will add a checkbox.

1<ul>
2 <li v-for="todo in todos" class="flex">
3 <label>
4 <input type="checkbox" class="nes-checkbox">
5 <span>&nbsp</span>
6 </label>
7 {{todo.text}}
8 <div class="space"></div>
9 <button class="nes-btn is-error padding" v-on:click="removeTodo(todo.id)">X</button>
10 </li>
11</ul>

We put the checkbox inside a label and we add a span with a space because NES.css has a bug, and if you don't do it this way, it doesn't show the checkbox. Well, here we have to do two things, first, we want that every time the checkbox is pressed, change the todo state from done: false todone: true and vice versa, so what do we need? you guessed, an event listener:

1<input type="checkbox" class="nes-checkbox" v-on:click="check(todo)">

And we add the check method:

1check(todo){
2 todo.done = !todo.done
3}

Marking TODOs as done And that's it, we're done, right? well, not quite, there is a small bug in our app. Let's do this, let's change one of our TODOs from done: false a done: true

1data: {
2 title: 'TODOS',
3 todos: [
4 {text: 'todo 1', done: false, id: Date.now()},
5 {text: 'todo 2', done: true, id: Date.now() + 1}
6 ]
7}

Bug There's the bug, our checkbox is not "synchronized" with the state of our TODO, so we have to use something called v-bind so that the checked property of the input is bind with the done property of our TODO, the syntax is this: v-bind:checked ="todo.done"

1<input type="checkbox" class="nes-checkbox" v-on:click="check(todo)" v-bind:checked="todo.done">

Bug Fixed We're about to finish, we just need to add a little visual detail, we want that if the TODO is marked as done, strike the text like this: Last details This is pretty easy to do, we just use v-if and v-else

1<ul>
2 <li v-for="todo in todos" class="flex">
3 <label>
4 <input type="checkbox" class="nes-checkbox">
5 <span>&nbsp</span>
6 </label>
7 <!-- WE DELETE THIS: {{todo.text}} -->
8 <del v-if="todo.done">{{ todo.text }}</del>
9 <span v-else>{{ todo.text }}</span>
10 <div class="space"></div>
11 <button class="nes-btn is-error padding" v-on:click="removeTodo(todo.id)">X</button>
12 </li>
13</ul>

And now we are done. App finished

Refactoring our App

I know I said that we had finished, but we can refactor the code a little bit, Vue has something called v-model, which allows us to replace the v-bind and the v-on:click in our checkbox. According to the vue documentation this:

1<input v-model=“searchText”>

It is the same as this:

1<input
2 v-bind:value="searchText"
3 v-on:input="searchText = $event.target.value"
4>

So let’s refactor our app. We replace v-on:click="check(todo)" and v-bind:checked="todo.done" for v-model="todo.done", and now we can also delete the check method.

1<input type="checkbox" class="nes-checkbox" v-model="todo.done">

Now we've really finished.

As you can see, Vue it's very simple and easy to learn. To get started, all you need is familiarity with HTML and some JavaScript. If you have experience with other frameworks such as React or Angular the process of learning Vue will be much simpler.

I hope you liked it.