Welcome folks today in this post we will making a todo app in vue.js 2020
all the source code of the application is given below. A step by step youtube video is shown below.
Get Started
Create the Vue.js Project inside your project directory by the following command
vue create todoapp
Now go to the project Directory and run the following commands on command line
cd todoapp
npm run serve
Now your vue.js app will be started on port 8080
Now open the text editor and edit the App.vue
file like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<template> <div> <AddTodo/> <Todos/> </div> </template> <script> import Todos from './components/Todos'; import AddTodo from './components/AddTodo' export default { name: 'App', data(){ return { todos:[ { id:1, title:"todo 1", completed:false }, { id:2, title:"todo 2", completed:true }, { id:3, title:"todo 3", completed:false }, { id:4, title:"todo 4", completed:true } ] } }, components: { Todos, AddTodo }, </script> |
Reading Todos
Now inside this App.vue
we have included two custom components inside our template which is Todos
component and the AddTodo
component which will be responsible for adding a new Todo from a form.
Now inside your components folder you need to create these components.
Todos.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<template> <div> <div v-bind:key="todo.id" v-for="todo in todos"> <TodoItem v-bind:todo="todo" /> </div> </div> </template> <script> import TodoItem from './TodoItem.vue'; export default { name: "Todos", components: { TodoItem }, props: ["todos"] } </script> <style scoped> </style> |
Here we are taking all the todos which are present and we are looping through it by using the v-for
directive and here again we are using a custom component again which is TodoItem
component
TodoItem.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<template> <div class="container"> <div class="todo-item" v-bind:class="{'is-complete':todo.completed}"> <p> <input type="checkbox" v-on:change="markComplete" v-bind:checked="todo.completed"> {{todo.title}} <button class="del">x</button> </p> </div> </div> </template> <script> export default { name: "TodoItem", props: ["todo"], methods: { markComplete() { this.todo.completed = !this.todo.completed; } } } </script> <style scoped> .todo-item { background: #f4f4f4; padding: 10px; margin:20px; border-bottom: 1px #ccc dotted; } .is-complete { text-decoration: line-through; } .del { background: #ff0000; color: #fff; border: none; padding: 5px 9px; border-radius: 50%; cursor: pointer; float: right; } </style> |
Now if you run the app it should look something like this as shown below
Now we need to create AddTodo
component which will contain a simple form to submit a new Todo inside the app
AddTodo.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<template> <div class="container"> <br> <h1 class="text-center">Todos App</h1> <br><br> <form> <div class="form-group"> <input required type="text" v-model="title" placeholder="title" name="title" id="" class="form-control"> </div> <div class="form-group"> <button class="btn btn-block btn-danger"> Add Todo </button> </div> </form> </div> </template> <script> export default { name: "AddTodo", data() { return { title: '' } } } </script> <style scoped> </style> |
Now if you run the app you will see the form also
Adding Todo
Now we will be adding a new Todo
inside the app by making the form work. To do that we will be adding a @submit
directive to the form like this
1 2 3 4 5 6 7 8 9 |
<form @submit="addTodo"> <div class="form-group"> <input required type="text" v-model="title" placeholder="title" name="title" id="" class="form-control"> </div> <div class="form-group"> <button class="btn btn-block btn-danger"> Add Todo </button> </div> |
Now we need to define this function which will execute once the form submits. Now inside the script section copy paste this code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
export default { name: "AddTodo", data() { return { title: '' } }, methods: { addTodo(e) { e.preventDefault() const newTodo = { id:Date.now(), title: this.title, completed: false } // Send up to parent this.$emit('add-todo', newTodo); this.title = ''; } } } |
So now inside this we are making a new Todo
from the values which are submitted namely id
which is auto generated by using the Date
function and the title
of the todo and a third property completed
which is default set to false
Now after that we are emitting a new event add-todo
which will automatically go to the parent element in this case App.vue
So now we need to catch this event which is passed from the child component
1 |
<AddTodo v-on:add-todo="addTodo"/> |
Here we are using a special directive v:on
to get the event which is sent and now we have defined a custom method AddTodo
which will be executed to add a Todo
App.vue
1 2 3 |
addTodo(newTodo){ this.todos = [...this.todos,newTodo] } |
Deleting Todos
Now we will be using the same concept to delete the todos as well for this go to the TodoItem.vue
file and make some changes like below
TodoItem.vue
1 |
<button @click="$emit('del-todo', todo.id)" class="del">x</button> |
So here we are assigning a button click listener by using @click
and we are emitting the event del-todo
So now we need to catch this in Todos.vue
file
Todos.vue
1 |
<TodoItem v-bind:todo="todo" v-on:del-todo="$emit('del-todo', todo.id)" /> |
And now again we need to emit this in App.vue
parent component like this
App.vue
1 |
<Todos v-bind:todos="todos" v-on:del-todo="deleteTodo"/> |
Now we need to define this custom function deleteTodo
which will be executed to actually delete the todo
from the todos
array
1 2 3 |
deleteTodo(id){ this.todos = this.todos.filter((todo) => todo.id !== id) } |
Now inside this block of code we are using the es6
high order function filter
to delete the todo from the array.
Full Source Code
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
<template> <div> <AddTodo v-on:add-todo="addTodo"/> <Todos v-bind:todos="todos" v-on:del-todo="deleteTodo"/> </div> </template> <script> import Todos from './components/Todos'; import AddTodo from './components/AddTodo' export default { name: 'App', data(){ return { todos:[ { id:1, title:"todo 1", completed:false }, { id:2, title:"todo 2", completed:true }, { id:3, title:"todo 3", completed:false }, { id:4, title:"todo 4", completed:true } ] } }, components: { Todos, AddTodo }, methods:{ addTodo(newTodo){ this.todos = [...this.todos,newTodo] }, deleteTodo(id){ this.todos = this.todos.filter((todo) => todo.id !== id) } } }; </script> |
AddTodo.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<template> <div class="container"> <br> <h1 class="text-center">Todos App</h1> <br><br> <form @submit="addTodo"> <div class="form-group"> <input required type="text" v-model="title" placeholder="title" name="title" id="" class="form-control"> </div> <div class="form-group"> <button class="btn btn-block btn-danger"> Add Todo </button> </div> </form> </div> </template> <script> export default { name: "AddTodo", data() { return { title: '' } }, methods: { addTodo(e) { e.preventDefault() const newTodo = { id:Date.now(), title: this.title, completed: false } // Send up to parent this.$emit('add-todo', newTodo); this.title = ''; } } } </script> <style scoped> </style> |
Todos.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<template> <div> <div v-bind:key="todo.id" v-for="todo in todos"> <TodoItem v-bind:todo="todo" v-on:del-todo="$emit('del-todo', todo.id)" /> </div> </div> </template> <script> import TodoItem from './TodoItem.vue'; export default { name: "Todos", components: { TodoItem }, props: ["todos"] } </script> <style scoped> </style> |
TodoItem.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<template> <div class="container"> <div class="todo-item" v-bind:class="{'is-complete':todo.completed}"> <p> <input type="checkbox" v-on:change="markComplete" v-bind:checked="todo.completed"> {{todo.title}} <button @click="$emit('del-todo', todo.id)" class="del">x</button> </p> </div> </div> </template> <script> export default { name: "TodoItem", props: ["todo"], methods: { markComplete() { this.todo.completed = !this.todo.completed; } } } </script> <style scoped> .todo-item { background: #f4f4f4; padding: 10px; margin:20px; border-bottom: 1px #ccc dotted; } .is-complete { text-decoration: line-through; } .del { background: #ff0000; color: #fff; border: none; padding: 5px 9px; border-radius: 50%; cursor: pointer; float: right; } </style> |
DOWNLOAD SOURCE CODE