You are currently viewing Creating a Todo App with Vuejs: A Practical Tutorial

Creating a Todo App with Vuejs: A Practical Tutorial

Vue.js is a progressive JavaScript framework used for building user interfaces. One of its core strengths is its flexibility and ease of use, which allows developers to quickly build and manage dynamic applications. In this practical tutorial, we will create a simple yet functional Todo app using Vue.js. This application will allow users to add, display, update, and delete todos, providing a hands-on experience with Vue.js components, state management, and event handling.

A Todo app is a classic beginner project that demonstrates fundamental web development concepts. By building this application, you will gain a solid understanding of how to structure Vue.js projects, manage component state, and implement CRUD (Create, Read, Update, Delete) operations. This comprehensive guide will take you step-by-step through the process, ensuring you have all the necessary knowledge to create your own Vue.js applications.

Setting Up the Development Environment

Installing Vue CLI

Before we start building our Todo app, we need to set up our development environment. Vue CLI (Command Line Interface) is a powerful tool that helps you create and manage Vue projects. To install Vue CLI, you must have Node.js and npm installed on your computer. Once you have these prerequisites, open your terminal and run the following command:

npm install -g @vue/cli

This command installs Vue CLI globally on your system, allowing you to use the vue command to create and manage Vue projects.

Creating a New Vue Project

With Vue CLI installed, you can now create a new Vue project. Run the following command in your terminal:

vue create todo-app

You will be prompted to select a preset for your project. Choose the default preset, which includes Babel and ESLint. Once the setup is complete, navigate into the project directory:

cd todo-app

To start the development server, run:

npm run serve

This command will compile your application and start a local development server. You can view your application in the browser at http://localhost:8080.

Creating the Todo App Structure

Defining the App Component

The App.vue file is the root component of our Vue application. Let’s start by defining the basic structure of our Todo app in App.vue:

<template>

  <div id="app">

    <h1>Todo App</h1>

    <AddTodo @add-todo="addTodo" />

    <ul>

      <TodoItem
        v-for="(todo, index) in todos"
        :key="index"
        :todo="todo"
        @delete-todo="deleteTodo(index)"
      />

    </ul>

  </div>

</template>

<script>

import AddTodo from './components/AddTodo.vue';
import TodoItem from './components/TodoItem.vue';

export default {
  name: 'App',
  components: {
    AddTodo,
    TodoItem
  },
  data() {

    return {
      todos: []
    };

  },
  methods: {

    addTodo(newTodo) {
      this.todos.push(newTodo);
    },

    deleteTodo(index) {
      this.todos.splice(index, 1);
    }

  }

};

</script>

<style>

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  text-align: center;
  margin-top: 60px;
}

</style>

In this code, we define a root component that includes a heading, the AddTodo component for adding new todos, and a list of TodoItem components for displaying each todo. We also define two methods: addTodo to add a new todo to the list and deleteTodo to remove a todo from the list.

Creating the TodoItem Component

Next, let’s create the TodoItem component. Create a new file named TodoItem.vue in the src/components directory and add the following code:

<template>

  <li>
    <span>{{ todo.text }}</span>
    <button @click="$emit('delete-todo')">Delete</button>
  </li>

</template>

<script>

export default {
  name: 'TodoItem',
  props: {
    todo: {
      type: Object,
      required: true
    }
  }
};

</script>

<style scoped>

li {
  list-style: none;
  margin: 10px 0;
}

button {
  margin-left: 10px;
}

</style>

In this code, the TodoItem component receives a todo prop and emits a delete-todo event when the delete button is clicked. This event is captured by the parent component (App.vue), which removes the todo from the list.

Creating the AddTodo Component

Now, let’s create the AddTodo component. Create a new file named AddTodo.vue in the src/components directory and add the following code:

<template>

  <div>
    <input v-model="newTodo" @keyup.enter="submitTodo" placeholder="Add a new todo" />
    <button @click="submitTodo">Add</button>
  </div>

</template>

<script>

export default {

  name: 'AddTodo',
  data() {
    return {
      newTodo: ''
    };
  },
  methods: {

    submitTodo() {

      if (this.newTodo.trim() !== '') {
        this.$emit('add-todo', { text: this.newTodo });
        this.newTodo = '';
      }

    }

  }

};

</script>

<style scoped>

input {
  padding: 8px;
  margin-right: 5px;
}

button {
  padding: 8px;
}

</style>

In this code, the AddTodo component includes an input field and a button. When the user types a new todo and presses enter or clicks the button, the submitTodo method is called. This method emits an add-todo event with the new todo as payload and clears the input field.

Managing State in Vue

Using Data Properties

In Vue.js, component state is managed using data properties. In our App.vue component, we define a todos data property to hold the list of todos. This state is reactive, meaning any changes to it will automatically update the DOM.

Handling Methods and Events

Methods in Vue.js are defined in the methods option. These methods can be bound to DOM events using the @ directive. In our App.vue component, the addTodo and deleteTodo methods are responsible for updating the state.

When a new todo is added, the AddTodo component emits an add-todo event, which is captured by the parent component. The addTodo method is then called, adding the new todo to the todos array. Similarly, when the delete button in a TodoItem component is clicked, it emits a delete-todo event, which triggers the deleteTodo method in the parent component.

Implementing CRUD Operations

Adding a New Todo

To add a new todo, the AddTodo component captures user input and emits an add-todo event. The parent component handles this event and updates the todos state. This process is illustrated in the following code:

<template>

  <div id="app">

    <h1>Todo App</h1>

    <AddTodo @add-todo="addTodo" />

    <ul>

      <TodoItem
        v-for="(todo, index) in todos"
        :key="index"
        :todo="todo"
        @delete-todo="deleteTodo(index)"
      />

    </ul>

  </div>

</template>

<script>

import AddTodo from './components/AddTodo.vue';
import TodoItem from './components/TodoItem.vue';

export default {

  name: 'App',
  components: {
    AddTodo,
    TodoItem
  },
  data() {

    return {
      todos: []
    };

  },
  methods: {

    addTodo(newTodo) {
      this.todos.push(newTodo);
    },

    deleteTodo(index) {
      this.todos.splice(index, 1);
    }

  }

};

</script>

Displaying Todos

Todos are displayed using the v-for directive to iterate over the todos array. Each todo is passed as a prop to the TodoItem component, which renders the todo text and provides a delete button.

<ul>

  <TodoItem
    v-for="(todo, index) in todos"
    :key="index"
    :todo="todo"
    @delete-todo="deleteTodo(index)"
  />

</ul>

Updating a Todo

Updating a todo can be implemented by adding an edit button and an input field in the TodoItem component. Here is an example:

<template>

  <li>

    <span v-if="!isEditing">{{ todo.text }}</span>
    <input v-else v-model="newText" @keyup.enter="saveTodo" />

    <button @click="isEditing = true" v-if="!isEditing">Edit</button>
    <button @click="saveTodo" v-else>Save</button>
    <button @click="$emit('delete-todo')">Delete</button>

  </li>

</template>

<script>

export default {
  name: 'TodoItem',
  props: {
    todo: {
      type: Object,
      required: true
    }
  },
  data() {

    return {
      isEditing: false,
      newText: this.todo.text
    };

  },
  methods: {

    saveTodo() {
      this.$emit('update-todo', this.newText);
      this.isEditing = false;
    }

  }

};

</script>

In this code, the TodoItem component includes an edit button that toggles the isEditing state. When editing, an input field is displayed, allowing the user to update the todo text. The saveTodo method emits an update-todo event with the updated text.

To handle this event in the parent component, update App.vue:

<template>

  <div id="app">

    <h1>Todo App</h1>
    <AddTodo @add-todo="addTodo" />

    <ul>

      <TodoItem
        v-for="(todo, index) in todos"
        :key="index"
        :todo="todo"
        @delete-todo="deleteTodo(index)"
        @update-todo="updateTodo(index, $event)"
      />

    </ul>

  </div>

</template>

<script>

import AddTodo from './components/AddTodo.vue';
import TodoItem from './components/TodoItem.vue';

export default {
  name: 'App',
  components: {
    AddTodo,
    TodoItem
  },
  data() {

    return {
      todos: []
    };

  },
  methods: {

    addTodo(newTodo) {
      this.todos.push(newTodo);
    },
    deleteTodo(index) {
      this.todos.splice(index, 1);
    },
    updateTodo(index, newText) {
      this.todos[index].text = newText;
    }

  }

};
</script>

Deleting a Todo

Deleting a todo is handled by emitting a delete-todo event from the TodoItem component and removing the todo from the todos array in the parent component:

<template>

  <li>
    <span>{{ todo.text }}</span>
    <button @click="$emit('delete-todo')">Delete</button>
  </li>

</template>

<template>

  <div id="app">

    <h1>Todo App</h1>

    <AddTodo @add-todo="addTodo" />

    <ul>
      <TodoItem
        v-for="(todo, index) in todos"
        :key="index"
        :todo="todo"
        @delete-todo="deleteTodo(index)"
      />
    </ul>

  </div>

</template>

<script>

import AddTodo from './components/AddTodo.vue';
import TodoItem from './components/TodoItem.vue';

export default {
  name: 'App',
  components: {
    AddTodo,
    TodoItem
  },
  data() {

    return {
      todos: []
    };

  },
  methods: {

    addTodo(newTodo) {
      this.todos.push(newTodo);
    },

    deleteTodo(index) {
      this.todos.splice(index, 1);
    }

  }

};

</script>

Styling the Todo App

Styling is an essential part of creating an appealing and user-friendly application. In Vue.js, you can apply scoped styles to individual components to prevent style leakage. Here is an example of styling our Todo app:

App.vue:

<style>

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  text-align: center;
  margin-top: 60px;
}

h1 {
  color: #42b983;
}

ul {
  padding: 0;
  max-width: 400px;
  margin: 0 auto;
}

li {
  background: #f9f9f9;
  margin: 5px 0;
  padding: 10px;
  border-radius: 4px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

</style>

AddTodo.vue:

<style scoped>

input {
  padding: 8px;
  margin-right: 5px;
  width: 70%;
  border: 1px solid #ccc;
  border-radius: 4px;
}

button {
  padding: 8px;
  border: none;
  background: #42b983;
  color: white;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background: #369971;
}

</style>

TodoItem.vue:

<style scoped>

button {
  padding: 4px 8px;
  border: none;
  background: #ff4d4d;
  color: white;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background: #d93636;
}

</style>

These styles provide a clean and modern look to our Todo app, enhancing the user experience.

Conclusion

In this practical tutorial, we built a simple yet functional Todo app using Vue.js. We covered the basics of setting up a Vue project with Vue CLI, creating and managing Vue components, and implementing CRUD operations. We also discussed state management, event handling, and styling components.

By following this guide, you have gained a solid understanding of how to create and manage Vue.js applications. The concepts and techniques learned here can be applied to more complex projects, allowing you to build powerful and dynamic web applications with Vue.js.

Additional Resources

To further expand your knowledge of Vue.js and enhance your development skills, here are some additional resources:

  1. Vue.js Documentation: The official Vue.js documentation is a comprehensive resource for understanding the framework’s capabilities and usage. Vue.js Documentation
  2. Vue Mastery: An excellent platform offering tutorials and courses on Vue.js. Vue Mastery
  3. Vue School: Another great resource for learning Vue.js through video courses. Vue School
  4. Books: Books such as “The Majesty of Vue.js” by Alex Kyriakidis and Kostas Maniatis provide in-depth insights and practical examples.
  5. Community and Forums: Join online communities and forums like Vue Forum, Reddit, and Stack Overflow to connect with other Vue developers, ask questions, and share knowledge.

By leveraging these resources and continuously practicing, you’ll become proficient in Vue.js and be well on your way to developing impressive and functional web applications.

Leave a Reply