Vue.js ToDo-List Tutorial

25.02.2021 Ricky Elfner
Mobile vuejs foss javascript frontend framework handson tutorial howto

For this tutorial we assume that you already have some experience with Vue.js and know how to use an application which is built with different components. In case you are not entirely sure, you can read beforehand our blog post about the basics in Vue.js . This explains the basics for this tutorial.

Create the project

Use the terminal to open the folder in which the app is to be created. There you run the command to create the project. The standard settings of Vue.js can be used for this tutorial.

$ vue create vue-todo-app

If you also want to carry out this tutorial with VS Code, you can start it with the command code .. For our to-do list, you can delete the standard component HelloWorld.vue, as well as the the default image files and all code in App.vue. After that, you should just do that have the basic structure of a component.

<template>
  <div id="app">
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {
  }
}
</script>

<style>

</style>

Creating the components

It is best to use the same basic structure as a template for all components that you need for a to-do list. With the difference, of course, that the name has to be renamed according to the component. Now we need three components for our app. ToDoItem.vue, which shows the individual items in the ToDo list. Then you still need the list ToDos.vue, which contains the items. And finally, you'll need one more component for the button AddToDoButton.vue.

The list

You can add a heading to the component ToDos.vue so that you can then import this component into the app and recognize that it is working as intended.

// ToDos.vue
<template>
    <div>
        <h1>Meine To-Do-Liste</h1>
    </div>
</template>

In the App.vue component you can now import the ToDos.vue component and use it directly. For the initial status of your to-do list, you can also enter data within the <script> section to determine the array. The integration of the data comes a step later.

// App.vue
<template>
    <div id="app">
        <ToDos />
    </div>
</template>

<script>
    import ToDos from './components/ToDos'

    export default {
    name: 'App',
    components: {
    ToDos
    },
    data() {
        return {
            todoEntries: [
                {
                    id: 1,
                    title: 'Infos sammeln',
                    completed: false
                },
                {
                    id: 2,
                    title: 'Test Projekt erstellen',
                    completed: false
                },
                {
                    id: 3,
                    title: 'Blog Beitrag schreiben',
                    completed: false
                },
                ],
            }
        },
    }
</script>

Since you now also want to use the ToDo entries, you have to transfer this to the ToDos.vue component. So that component also knows that it can receive this as Properties, you have to do this in the export statement. In the same step you also import the ToDoItem.vue component.

//ToDos.vue
<script>
import ToDoItem from "./ToDoItem";

export default {
  name: 'ToDos',
  components: {
      ToDoItem
  },
  props: [
      "todoEntries"
  ]
}
</script>

In the next step you have to define the properties to transfer the entries. To do this, use the direct v-bind within App.vue to transfer the array.

    //App.vue
    <ToDos v-bind:todoEntries="todoEntries"/>

The item

In order to display the entries of this list, you have to loop through the array and each time call the component ToDoItem.vue. To do this, however, we must first define that ToDoItem has the Properties to receives a ToDo entry and displays the title of it.

// ToDoItem.vue
<template>
  <div>
    {{ todoItem.title }}
  </div>
</template>

<script>

export default {
  name: 'ToDoItem',
  components: {
  },props: [
    "todoItem"
  ]
}

Now create a list within the ToDos.vue component using the v-for directive. This corresponds to a for loop. In order to be able to recognize each entry later, you have to define another key, which is the id of an entry. The v-bind directive is also used for this. Now you can see your initial values in our browser!

// ToDos.vue
<template>
  <div>
      <h1>Meine To-Do-Liste</h1>
      <ul>
          <li v-bind:key="item.id" v-for="item in todoEntries">
              <ToDoItem v-bind:todoItem="item" />
          </li>   
      </ul>
  </div>
</template>

Adding entries

Now, of course, you also need an input field and a button to add new to-do's to the list. To do this, create a form that reacts to the Submit event, which is triggered by the button. As soon as this event occurs, the method addToDo is called. Our data is synchronised with the entries in the input field. This is done using the v-model directive.

//AddToDoButton.vue
<template>
  <div>
    <form @submit="addToDo">
      <input type="text" v-model="title" name="title">
      <button type="submit">Hinzufügen</button>
    </form>
  </div>
</template>

Then you still need the implementation of the function addToDo. The first thing you have to do is to specify that the event does not perform the standard behavior of a submit. Instead, we want to pass on the data into our array toDoEintries within the App.vue component. To do this, you first need to create a new ToDo item. For the ID we use a package called uuid. With this it is possible to automatically create random ids. If you have not yet installed this package, you can do this via your terminal in your root directory.

$ nmp install uuid;

Now you can use the function to create an id for the new item. Use the value of the input field for the title. Set the state completed to false by default. After that you have to create a custom event that can be accessed and also transfers the newly created item. The following example is about the event add-todo-event. It is important to remember to clear the content of the input fields again.

<script>

    import { v4 as uuidv4 } from 'uuid';

    export default {
    name: 'AddToDoButton',
    data() {
        return {
            title: ''
        }
    },
    methods: {
        addToDo(event){
            event.preventDefault();
        
            const newToDoObject = {
                id: uuidv4(),
                title: this.title,
                completed: false
            }
            
            this.$emit("add-todo-event", newToDoObject);
            this.title= '';
        }
    }
}
</script>

So that you can also use the last component, you have to import and add it as usual in the component App.vue. Furthermore, the component should "listen" to the previously created event and, as soon as it is triggered, execute a method addToDoItem. This is defined with @dd-todo-event.

// App.vue
    <template>
      <div id="app">
        <ToDos v-bind:todoEntries="todoEntries"/>
        <AddToDoButton @add-todo-event="addToDoItem"/>
      </div>
    </template>

The method addToDoItem accepts the new item as a parameter. Using the spread syntax, the received object added to the array.

  methods: {
    addToDoItem(newToDoItem) {
      this.todoEntries = [...this.todoEntries, newToDoItem];
    }
  },

Mark entries as done

Typically, you would also like to have the option of marking the entries in a ToDo list as completed. For this we add the class completed to the ToDoItem.vue, depending on whether completed is true or false. This state is changed by the method markComplete, which is triggered by the event click.

// ToDoItem.vue
    <template>
      <div v-bind:class="{ 'completed' : todoItem.completed }">
        <p @click="markComplete">{{ todoItem.title }}</p>
      </div>
    </template>

The method simply inverts the state of the attribute completed.

  methods: {
    markComplete(){
      this.todoItem.completed = !this.todoItem.completed
    }
  }

The last adjustment is the style adjustment for the class completed.

    <style>
      .completed {
        text-decoration: line-through;
      }
    </style>

Deleting entries

Last but not least, you can implement the delete function for individual entries in the list. It works in a similar way like creating a new object, only here the data has to be passed on via two components. To do this, we add a button to the ToDoItem.vue component. This directly creates a custom event and passes the id further upwards as soon as this is clicked.

//ToDoItem.vue
    <button @click="$emit('delete-todo-event', todoItem.id)">Löschen</button>

Next you have to receive this event in the component ToDos.vue and use another event to also pass upwards so that the ID can be further processed within the App.vue component. Even this time you can do this directly using inline notation.

//ToDos.vue
    <ToDoItem v-bind:todoItem="item" @delete-todo-event="$emit('delete-todo-event', item.id)"/>

Finally, you have the ID in the desired location. This time, it's best to use a method call as soon as the event delete-todo-event is triggered.

    <ToDos v-bind:todoEntries="todoEntries" @delete-todo-event="deleteToDoItem"/>

In our example, the method delteToDoItem is called and takes the ID as a parameter. Within the method we recompose the array with all items that do not have the same ID as the parameters variable.

  methods: {
    addToDoItem(newToDoItem) {
      this.todoEntries = [...this.todoEntries, newToDoItem];
    },
    deleteToDoItem(toDoId){
      this.todoEntries = this.todoEntries.filter(item => item.id !== toDoId)
    }
  },

Congratulations! You have now created your own to-do list using Vue.js.


This text was automatically translated with our golang markdown translator.

Ricky Elfner - thinker, survivor, gadget collector. He is always on the lookout for new innovational potentials, as well as tech news, so that he can always write about current topics.