Building a Simple To-Do App with Vue 3
Vue is a popular JavaScript framework for building user interfaces. With the release of Vue 3, there are a lot of new features and improvements to explore. In this tutorial, we will show you how to build a simple to-do app using Vue 3.
Setup
To get started, let's create a new Vue project using the Vue CLI. Open your terminal and run the following commands:
copy
npm install -g @vue/cli
vue create todo-app
This will install the Vue CLI globally and create a new Vue project called `todo-app`.
Once the project is created, navigate to the project directory and run `npm run serve` to start the development server.
Creating Components
The first step in building our to-do app is to create some components. In Vue, components are reusable blocks of code that can be used to build user interfaces.
Let's create two components: `TodoList` and `TodoItem`.
TodoList
The `TodoList` component will be the main component of our app. It will be responsible for rendering the list of to-do items and handling user input.
Create a new file called TodoList.vue in the src/components directory and add the following code:
copy
<template>
<div>
<h1>To-Do List</h1>
<input type="text" v-model="newTodo" @keyup.enter="addTodo">
<ul>
<todo-item v-for="(todo, index) in todos" :key="index" :todo="todo"></todo-item>
</ul>
</div>
</template>
<script>
import { ref } from 'vue';
import TodoItem from './TodoItem.vue';
export default {
name: 'TodoList',
components: {
TodoItem
},
setup() {
const newTodo = ref('');
const todos = ref([]);
const addTodo = () => {
todos.value.push(newTodo.value);
newTodo.value = '';
}
return {
newTodo,
todos,
addTodo
}
}
}
</script>
In this code, we are using the `ref` function from the Composition API to create reactive variables for `newTodo` and `todos`. We are also defining an `addTodo` function that will push a new to-do item to the `todos` array and clear the `newTodo` variable.
The `TodoList` component also includes an input field that will be used to add new to-do items, and a loop that will render a `TodoItem` component for each to-do item in the `todos` array.
TodoItem
The `TodoItem` component will be responsible for rendering an individual to-do item and allowing the user to mark it as complete.
Create a new file called `TodoItem.vue` in the `src/components` directory and add the following code:
copy
<template>
<li :class="{ complete: isComplete }" @click="toggleComplete">{{ todo }}</li>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'TodoItem',
props: {
todo: String
},
setup(props) {
const isComplete = ref(false);
const toggleComplete = () => {
isComplete.value = !isComplete.value;
}
return {
isComplete,
toggleComplete
}
}
}
</script>
In this code, we are using the `ref` function from the Composition API to create a reactive variable for `isComplete`. We are also defining a `toggleComplete` function that will toggle the `isComplete` variable between `true` and `false`.
The `TodoItem` component also includes a prop called `todo`, which is a string that represents the to-do item that will be rendered.
In the `TodoList` component, we are using the `v-for` directive to loop through the `todos` array and render a `TodoItem` component for each item. We are passing the `todo` prop to each `TodoItem` component, which will be used to render the text of the to-do item.
With the `TodoList` and `TodoItem` components created, we have the basic structure of our to-do app. However, it still looks very plain and doesn't have much functionality beyond marking items as complete.
Add some styling and additional features
To make our app more user-friendly, we'll add some styling and additional features. We'll start by creating a new file called `App.vue` in the `src` directory and adding the following code:
copy
<template>
<div class="container">
<h1>My To-Do List</h1>
<TodoList :todos="todos" />
<div class="input-group">
<input type="text" v-model="newTodo" placeholder="Add a new to-do" />
<button @click="addTodo">Add</button>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
import TodoList from './components/TodoList.vue';
export default {
name: 'App',
components: {
TodoList
},
setup() {
const todos = ref([
'Buy groceries',
'Do laundry',
'Clean the house'
]);
const newTodo = ref('');
const addTodo = () => {
if (newTodo.value.trim() !== '') {
todos.value.push(newTodo.value.trim());
newTodo.value = '';
}
}
return {
todos,
newTodo,
addTodo
}
}
}
</script>
<style>
.container {
max-width: 600px;
margin: 0 auto;
}
h1 {
text-align: center;
}
.input-group {
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
input {
flex: 1;
margin-right: 10px;
padding: 5px;
font-size: 1.2rem;
}
button {
background-color: #4CAF50;
color: white;
padding: 5px 10px;
border-radius: 5px;
font-size: 1.2rem;
cursor: pointer;
}
</style>
In this code, we are using the `ref` function to create a reactive variable for `todos` and `newTodo`. We are also defining a `addTodo` function that will add the value of `newTodo` to the `todos` array and clear the input field.
In the template, we are using the `TodoList` component to render the list of to-dos. We are passing the `todos` array as a prop to the `TodoList` component. We are also rendering an input field and a button that will allow the user to add new to-do items to the list.
To style our app, we are using some basic CSS to center the title and form, as well as to style the input field and button.
With these changes, our to-do app is now much more user-friendly and functional. Users can add new to-do items to the list and mark items as complete. However, there is still room for improvement.
Add the ability to delete to-do items
One feature we could add is the ability to delete to-do items. We could add a delete button to each `TodoItem` component, which would remove the item from the `todos` array when clicked.
To add this functionality, we'll start by updating the TodoItem component to include a delete button
copy
<template>
<div :class="{'completed': completed}">
<input type="checkbox" v-model="completed" @change="updateTodo" />
<span>{{ text }}</span>
<button class="delete-button" @click="deleteTodo">✖</button>
</div>
</template>
<script>
export default {
name: 'TodoItem',
props: ['text', 'completed', 'update'],
methods: {
updateTodo() {
this.update(this.text, this.completed);
},
deleteTodo() {
this.$emit('delete', this.text);
}
}
}
</script>
<style>
.completed {
text-decoration: line-through;
}
.delete-button {
background-color: #ff4136;
color: white;
border: none;
border-radius: 50%;
padding: 5px;
font-size: 1.2rem;
cursor: pointer;
margin-left: 10px;
}
</style>
In this code, we have added a delete button to the `TodoItem` component. The button has a red background and an 'X' symbol. When clicked, the button will emit a custom `delete` event and pass the `text` prop as a payload.
Update the TodoList
Next, we'll update the TodoList component to listen for the delete event and remove the corresponding item from the todos array
copy
<template>
<div>
<TodoItem
v-for="(todo, index) in todos"
:key="index"
:text="todo.text"
:completed="todo.completed"
@update="updateTodo"
@delete="deleteTodo"
/>
</div>
</template>
<script>
import { reactive } from 'vue';
import TodoItem from './TodoItem.vue';
export default {
name: 'TodoList',
props: ['todos'],
components: {
TodoItem
},
setup(props) {
const state = reactive({
todos: props.todos
});
const updateTodo = (text, completed) => {
const todo = state.todos.find(todo => todo.text === text);
todo.completed = completed;
}
const deleteTodo = (text) => {
const index = state.todos.findIndex(todo => todo.text === text);
state.todos.splice(index, 1);
}
return {
updateTodo,
deleteTodo
}
}
}
</script>
In this code, we are using the `reactive` function to create a reactive object called `state`, which contains a `todos` array that is initialized with the todos prop passed from the `App` component.
We have also added an `updateTodo` method that updates the `completed` property of the corresponding `TodoItem` in the `todos` array when called. Additionally, we have added a `deleteTodo` method that removes the corresponding item from the `todos` array when called.
Finally, we have updated the `TodoList` template to listen for the `delete` event emitted by the `TodoItem` component and call the `deleteTodo` method with the payload.
Summary
The article explains how to create a simple to-do list application using Vue 3 Composition API. It covers topics like creating components, handling user input, rendering dynamic lists, and adding functionality like updating and deleting to-do items. The tutorial provides a step-by-step guide with code examples and explanations for each step. The end result is a fully functional to-do list application that users can interact with.