VueJS is a modern Javascript framework that makes it easy to handle data flow, simply by including attributes in your HTML tags.
In this guide, we'll be building a simple todo list app to get up and running with VueJS.
Setup and Installation
There are two ways to setup Vue: through a NodeJS project, or by including a script inside of your HTML file. Since we're just starting out, we'll use a script inside of our index.html file.
We can set up our index.html file like this.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todooey - A Simple Todo List App</title>
<link rel="stylesheet" href="style.css">
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
</div>
</body>
</html>
In order to use Vue in our app, we need to create a new instance of Vue. We can do this using another script
tag before the closing body
tag.
<script>
new Vue( {
el: '#app',
});
</script>
Now, we're able to use Vue in our app!
Creating Our App
Before we add the functionality to our app with Vue, we'll create the basic HTML/CSS structure with static content.
Inside of our HTML file, we'll create the Add Todo input, as well as the Todo list and each item
<div class="container">
<h1 class="">My Todo List</h1>
<div class="card">
<div class="flex">
<input placeholder="Add new todo" />
<button>Add</button>
</div>
</div>
<div class="card">
<div class="card-inner">
<h2>Todo</h2>
<ul class="list">
<li class="list-item">
<div class="list-item-toggle"></div><span>Wash the car</span>
<div class="list-item-delete">X</div>
</li>
</ul>
</div>
</div>
</div>
Then, we'll add some basic styling to our app inside our style.css
file.
html,
body {
margin: 0;
padding: 0;
background: #faffff;
font-size: 16px;
}
* {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
color: #3d4855;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
}
.container {
padding: 24px 0;
max-width: 700px;
width: 100%;
margin: 0 auto;
}
.card {
border-radius: 4px;
box-shadow: 1px 1px 40px -10px #31505f30, 0px 1px 2px 0px #31505f30;
background: white;
margin-bottom: 24px;
}
.card-inner {
padding: 16px 24px;
}
.flex {
display: flex;
align-items: center;
justify-content: space-between;
}
input {
border-radius: 4px;
background: transparent;
border: none;
width: 100%;
padding: 14px;
font-size: 16px;
border: 1px solid transparent;
height: 100%;
display: block;
outline: none;
}
button {
background: #4fc08d;
padding: 10px 22px;
border: none;
color: white;
border-radius: 4px;
margin: 8px;
font-size: 16px;
cursor: pointer;
box-shadow: 1px 1px 15px -2px #212c4430;
transition: 0.15s;
}
button:hover {
background: #42aa7b;
}
button:disabled {
background: #e8e8e8;
color: #555;
box-shadow: none;
}
.list {
list-style: none;
margin: 0;
padding: 0;
}
.list-item {
padding: 12px 16px 12px 16px;
border: 1px solid #e8e8e8;
cursor: pointer;
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 6px;
border-radius: 4px;
}
.list-item:first-child {
border-top: 1px solid #e8e8e8;
}
.list-item-toggle {
border: 1px solid #e8e8e8;
border-radius: 999px;
height: 21px;
width: 21px;
margin-right: 16px;
}
.list-item-delete {
margin-left: auto;
color: tomato;
margin-top: -2px;
font-weight: bold;
text-decoration: none !important;
}
.list-item.completed {
border: 1px solid #4fc08d;
}
.list-item.completed span {
text-decoration: line-through;
}
.list-item.completed .list-item-toggle {
background: #4fc08d;
border: #4fc08d;
}
Using Vue to Add Functionality
Great! Now that our app is styled, we can begin using Vue to create a dynamic todo list.
Displaying Our Todo List
To display our todo list, we'll take advantage of Vue's 2-way data flow. Inside of our script
tag, we'll use Vue's data
object to create an array that will contain all our todo items.
<script>
new Vue( {
el: '#app',
data: {
items: [
{
id: 1,
name: 'Clean the fridge'
},
{
id: 2,
name: 'Walk the dogs'
},
]
}
});
</script>
Each todo item has a name and an ID, which will be used for removing items from the list later on.
Now that we have our data, we can display it in our list using the v-for
attribute, which is basically a forEach
loop that Vue uses.
<ul class="list">
<li class="list-item" v-for="item in reversedItems">
...
<span>{{ item.name }}</span>
...
</li>
</ul>
Using the v-for
attribute allows us to acces the item
property. We can display the name by using the double handlebars syntax: {{ item.name }}
.
Adding Todo Items
Now that our items display properly, we can work on adding new items to the list. Using Vue's methods
property, we can create a method that adds a new todo to the list.
First, let's create a new property inside our data
object, called newItem
.
<script>
new Vue( {
el: '#app',
data: {
newItem: '',
items: [...]
}
});
</script>
This will be the value that we enter into the Add Todo input.
In order to make sure that what we type in our input updates the newItem
value, we can take advantage of Vue's 2-way data flow, using the v-model
attribute. This means that whatever value we enter into the input will be persisted to the data
object.
<input v-model="newItem" placeholder="Add new todo" />
Since we now have our newItem
value stored, we can create a method to add that item to the list.
Beneath the data
object, we'll create a new methods
object with a function, addItem
.
<script>
new Vue( {
el: '#app',
data: {...},
methods: {
addItem: function() {
this.items.push({
id: this.items.length + 1,
name: this.newItem,
completed: false,
});
this.newItem = '';
},
},
});
</script>
Basically, when this function is called, we're taking the newItem
value and pushing it to the items
array. The, we're clearing out the newItem
value, which clears our Add Todo input.
Now, all we need to do is call the function when we click the Add button. We can use the v-on
attribute, or the @
symbol for short.
<button @click="addItem">Add</button>
Now, Vue will know to call the addItem
function when this button is clicked.
As something a little extra, we can also disable the button is there is no value in the input, using the :disabled
attribute. This tells Vue to apply the disabled attribute only if the expression inside the qoutes is true.
<button @click="addItem" :disabled="newItem.length === 0">Add</button>
Marking Items as Complete
The final thing that we need to do is add the ability to mark our items as complete.
To do this, we'll add a new property to each item in our array: the completed
property.
<script>
new Vue({
el: '#app',
data: {
items: [{
id: 1,
name: 'Clean the fridge',
completed: true,
},
{
id: 2,
name: 'Walk the dogs',
completed: false,
}]
}
});
</script>
Vue once again provides us with an attribute to dynamically change the class of an element, based on data in the Vue instance.
So, we can go to our list item and add the :class
attribute.
<li class="list-item" :class="{completed: item.completed}" v-for="item in reversedItems">
...
</li>
This tells Vue that it should apply the completed
class to the <li>
only if the item is completed (which we can tell by accessing the item.completed
property.
Now, our completed items should have a green outline. However, we still need to be able to mark them complete if they are not.
To do this, we'll create another method, called toggleComplete
.
<script>
new Vue( {
el: '#app',
data: {...},
methods: {
addItem: function() {...},
toggleComplete: function (item) {
item.completed = !item.completed;
}
},
});
</script>
Once we have our method, we can call it using the @click
attribute that Vue provides.
<li class="list-item" :class="{completed: item.completed}" v-for="item in reversedItems">
<div class="list-item-toggle" @click="toggleComplete(item)"></div>
...
</li>
Once again, we can pass in the item
object as a prop to the function, because Vue allows us to access it via the v-for
attribute.
Now, we can toggle each todo item between complete and uncomplete.
Deleting Todo Items
The final thing we need to do is allow ourselves to delete todo items. Once again, we'll use a method to accomplish this.
<script>
new Vue( {
el: '#app',
data: {...},
methods: {
addItem: function() {...},
toggleComplete: function (item) {...},
removeItem: function (itemID) {
this.items = this.items.filter((item) => newItem.id!== itemID);
}
},
});
</script>
In this function, we're accessing the itemID
prop (which is passed from the delete element) and setting the items
property to a new array, without the item we just deleted.
Now, we can call the function from our delete element.
<li class="list-item" :class="{completed: item.completed}" v-for="item in reversedItems">
...
<div class="list-item-delete" @click="removeItem(item.id)">X</div>
</li>
Tada! Now, we can succesfully delete our todo items!
Final Thoughts
So that's it! We've just build a functioning todo application using Vue. We learned how to call methods, access data and update data, all without any JS DOM manipulation.
You can find the full code for this app on Github.
If you liked this tutorial, I'd appreciate it if you could buy me a coffee! Or, follow me on Twitter ✌.
Top comments (2)
Hello! Thanks for this tutorial.
Under the step "display our to do list" you say to add this line:
li class="list-item" v-for="item in reversedItems"
I notice after adding that line, Chrome devtools is saying that reversedItem is undefined, which it is. Is that definition something you had meant to include earlier in the tutorial? That error seems to be the reason my list in not displaying. I noticed in the Github you linked, reversedItems might be defined around line 55, is this correct? If so, could you expand on what that code is doing? Thanks!
Good question, I was wondering this myself, it appears in the repo but not in the article, just a
computed()
propertygithub.com/jarodpeachey/vue-todo-l...